Skip to content

Commit

Permalink
Add update code stage
Browse files Browse the repository at this point in the history
  • Loading branch information
mikael-lindstrom authored and shyamsfo committed Jan 26, 2021
1 parent 1881370 commit 408f074
Show file tree
Hide file tree
Showing 11 changed files with 373 additions and 3 deletions.
3 changes: 2 additions & 1 deletion lambda-deployment-deck/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { lambdaDeploymentStage, initialize } from './deployLambda';
import { lambdaDeleteStage } from './deleteLambda';
import { lambdaRouteStage} from './routeLambda';
import { lambdaInvokeStage} from './invokeLambda';
import { lambdaUpdateCodeStage} from "./updateCodeLambda";

export const plugin: IDeckPlugin = {
initialize,
stages: [lambdaDeploymentStage, lambdaDeleteStage, lambdaRouteStage, lambdaInvokeStage],
stages: [lambdaDeploymentStage, lambdaDeleteStage, lambdaRouteStage, lambdaInvokeStage, lambdaUpdateCodeStage],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

.LambdaCodeUpdateStageConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import React from 'react';

import {
ExecutionDetailsTasks,
HelpContentsRegistry,
IStageTypeConfig,
} from '@spinnaker/core';

import { LambdaUpdateCodeExecutionDetails } from './LambdaUpdateCodeStageExecutionDetails';
import { LambdaUpdateCodeConfig, validate } from './LambdaUpdateCodeStageConfig';

export const initialize = () => {
HelpContentsRegistry.register('aws.lambdaDeploymentStage.lambda', 'Lambda Name');
};

export const lambdaUpdateCodeStage: IStageTypeConfig = {
key: 'Aws.LambdaUpdateCodeStage',
label: `AWS Lambda Update Code`,
description: 'Update code for a single AWS Lambda Function',
component: LambdaUpdateCodeConfig, // stage config
executionDetailsSections: [LambdaUpdateCodeExecutionDetails, ExecutionDetailsTasks],
validateFn: validate,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import React from 'react';

import {
FormikStageConfig,
FormValidator,
IFormikStageConfigInjectedProps,
IStage,
IStageConfigProps,
} from '@spinnaker/core';

import './LambdaUpdateCodeStage.less';
import {
s3BucketNameValidator,
} from '../utils/aws.validators';
import { UpdateCodeLambdaFunctionStageForm } from './components/UpdateCodeStageForm';

export function LambdaUpdateCodeConfig(props: IStageConfigProps) {
return (
<div className="LambdaUpdateCodeConfig">
<FormikStageConfig
{...props}
validate={validate}
onChange={props.updateStage}
render={(props: IFormikStageConfigInjectedProps) => <UpdateCodeLambdaFunctionStageForm {...props} />}
/>
</div>
);
}

export function validate(stageConfig: IStage) {
const validator = new FormValidator(stageConfig);

validator
.field('account', 'Account Name')
.required()

validator
.field('region', 'Region')
.required()

validator
.field('functionName', 'Lambda Function Name')
.required()

validator
.field('s3key', 'S3 Object Key')
.required()

validator
.field('s3bucket', 'S3 Bucket Name')
.required()
.withValidators(s3BucketNameValidator);

return validator.validateForm();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import React from 'react';

import {
ExecutionDetailsSection,
IExecutionDetailsSectionProps,
StageFailureMessage,
} from '@spinnaker/core';

export function LambdaUpdateCodeExecutionDetails(props: IExecutionDetailsSectionProps) {
const { stage, current, name } = props;
return (
<ExecutionDetailsSection name={name} current={current}>
<StageFailureMessage stage={stage} message={stage.outputs.failureMessage} />
<div>
<p> <b> Function Name: </b> {stage.outputs.functionName ? stage.outputs.functionName : "N/A"} </p>
<p> <b> Function ARN: </b> {stage.outputs.functionARN ? stage.outputs.functionARN : "N/A"} </p>
</div>
</ExecutionDetailsSection>
);
}

export namespace LambdaUpdateCodeExecutionDetails {
export const title = 'Lambda Update Code Stage';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import React from 'react';

import {
AccountService, CheckboxInput,
FormikFormField,
HelpField, IAccount, IAccountDetails,
IFormikStageConfigInjectedProps, IFormInputProps, IFunction, IRegion, ReactSelectInput, TextInput, useData
} from '@spinnaker/core';

export function UpdateCodeLambdaFunctionStageForm(props: IFormikStageConfigInjectedProps) {
const { values, errors } = props.formik;
const { functions } = props.application;

const { result: fetchAccountsResult, status: fetchAccountsStatus } = useData(
() => AccountService.listAccounts('aws'),
[],
[],
);

const onAccountChange = (fieldValue: any): void => {
props.formik.setFieldValue("region", null);
props.formik.setFieldValue("functionName", null);

props.formik.setFieldValue("account", fieldValue);
};

const onRegionChange = (fieldValue: any): void => {
props.formik.setFieldValue("functionName", null);
props.formik.setFieldValue("region", fieldValue);
};

const availableFunctions = values.account && values.region ?
functions.data
.filter((f: IFunction) => f.account === values.account)
.filter((f: IFunction) => f.region === values.region)
.map((f: IFunction) => f.functionName) :
[];

return (
<div className="form-horizontal">
<h4> Basic Settings </h4>
<FormikFormField
label="Account"
name="account"
onChange={onAccountChange}
required={true}
input={(inputProps: IFormInputProps) => (
<ReactSelectInput
{...inputProps}
clearable={false}
isLoading={fetchAccountsStatus === 'PENDING'}
stringOptions={fetchAccountsResult.map((acc: IAccount) => acc.name)}
/>
)}
/>
<FormikFormField
label="Region"
name="region"
onChange={onRegionChange}
input={(inputProps: IFormInputProps) => (
<ReactSelectInput
clearable={false}
disabled={ !(values.account) }
placeholder={
values.account ?
"Select..." :
"Select an Account..."
}
{...inputProps}
isLoading={fetchAccountsStatus === 'PENDING'}
stringOptions={fetchAccountsResult
.filter((acc: IAccountDetails) => acc.name === values.account)
.flatMap((acc: IAccountDetails) => acc.regions)
.map((reg: IRegion) => reg.name)
}
/>
)}
/>
<FormikFormField
label="Function Name"
name="functionName"
input={(inputProps: IFormInputProps) => (
<ReactSelectInput
clearable={false}
disabled={ !(values.account && values.region) }
placeholder={
values.account && values.region ?
"Select..." :
"Select an Account and Region..."
}
{...inputProps}
stringOptions={ availableFunctions }
/>
)}
/>
<FormikFormField
name="s3bucket"
label="S3 Bucket"
help={<HelpField id="aws.function.s3bucket" />}
input={props => <TextInput {...props} placeholder="S3 bucket name" />}
/>
<FormikFormField
name="s3key"
label="S3 Key"
help={<HelpField id="aws.function.s3key" />}
input={props => <TextInput {...props} placeholder="object.zip" />}
/>
<FormikFormField
name="publish"
label="Publish"
help={<HelpField id="aws.function.publish" />}
input={props => <CheckboxInput {...props} />}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

export * from './UpdateCodeStageForm';
4 changes: 4 additions & 0 deletions lambda-deployment-deck/src/updateCodeLambda/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

export * from './LambdaUpdateCodeStage';
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import com.amazon.aws.spinnaker.plugin.lambda.invoke.LambdaInvokeTask;
import com.amazon.aws.spinnaker.plugin.lambda.invoke.LambdaInvokeVerificationTask;
import com.amazon.aws.spinnaker.plugin.lambda.traffic.*;
import com.amazon.aws.spinnaker.plugin.lambda.updatecode.LambdaUpdateCodeStage;
import com.amazon.aws.spinnaker.plugin.lambda.updatecode.LambdaWaitForCacheCodeUpdateTask;
import com.amazon.aws.spinnaker.plugin.lambda.upsert.*;
import com.amazon.aws.spinnaker.plugin.lambda.utils.LambdaCloudDriverUtils;
import com.amazon.aws.spinnaker.plugin.lambda.verify.LambdaCacheRefreshTask;
Expand Down Expand Up @@ -75,7 +77,10 @@ public void registerBeanDefinitions(BeanDefinitionRegistry registry) {
Pair.of("Aws.LambdaInvokeStage", LambdaInvokeStage.class),
Pair.of("lambdaInvokeTask", LambdaInvokeTask.class),
Pair.of("lambdaInvokeVerifyTask", LambdaInvokeVerificationTask.class),
Pair.of("Aws.LambdaTrafficShaper", LambdaTrafficRoutingStage.class));
Pair.of("Aws.LambdaTrafficShaper", LambdaTrafficRoutingStage.class),
Pair.of("Aws.LambdaUpdateCodeStage", LambdaUpdateCodeStage.class),
Pair.of("lambdaWaitForCacheCodeUpdateTask", LambdaWaitForCacheCodeUpdateTask.class)
);
beanList.forEach( curr -> {
BeanDefinition lazyLoadCredentialsRepositoryDefinition = primaryBeanDefinitionFor(curr.getRight());
try {
Expand All @@ -98,6 +103,7 @@ public List<String> getPackagesToScan() {
"com.amazon.aws.spinnaker.plugin.lambda.upsert",
"com.amazon.aws.spinnaker.plugin.lambda.utils",
"com.amazon.aws.spinnaker.plugin.lambda.verify",
"com.amazon.aws.spinnaker.plugin.lambda.traffic");
"com.amazon.aws.spinnaker.plugin.lambda.traffic",
"com.amazon.aws.spinnaker.plugins.lambda.updatecode");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.amazon.aws.spinnaker.plugin.lambda.updatecode;

import com.amazon.aws.spinnaker.plugin.lambda.upsert.LambdaUpdateCodeTask;
import com.amazon.aws.spinnaker.plugin.lambda.verify.LambdaCacheRefreshTask;
import com.amazon.aws.spinnaker.plugin.lambda.verify.LambdaVerificationTask;
import com.netflix.spinnaker.orca.api.pipeline.graph.StageDefinitionBuilder;
import com.netflix.spinnaker.orca.api.pipeline.graph.TaskNode;
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;

@Component
@StageDefinitionBuilder.Aliases({"Aws.LambdaUpdateCodeStage"})
public class LambdaUpdateCodeStage implements StageDefinitionBuilder {
private static Logger logger = LoggerFactory.getLogger(LambdaUpdateCodeStage.class);

public LambdaUpdateCodeStage() {
logger.debug("Constructing Aws.LambdaCodeUpdateStage");
}

@Override
public void taskGraph(@Nonnull StageExecution stage, @Nonnull TaskNode.Builder builder) {
logger.debug("taskGraph for Aws.LambdaUpdateCodeStage");
builder.withTask("lambdaUpdateCodeTask", LambdaUpdateCodeTask.class);
builder.withTask("lambdaVerificationTask", LambdaVerificationTask.class);
builder.withTask("lambdaCacheRefreshTask", LambdaCacheRefreshTask.class);
builder.withTask("lambdaWaitForCacheTask", LambdaWaitForCacheCodeUpdateTask.class);
}
}
Loading

0 comments on commit 408f074

Please sign in to comment.