Skip to content

Commit

Permalink
feat(codebuild): Add support for static credentials (#1554)
Browse files Browse the repository at this point in the history
1. Add an AbstractEditCiCommand class that could be inherited from. For now only codebuild account
command extends this class. There could be more in the future.
2. Mark assume-role and account-id as not required so that the static credentials could be used directly
3. Add an API in halyard daemon to set custom fields for CI providers

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
Kaixiang-AWS and mergify[bot] committed Feb 28, 2020
1 parent 4ba334a commit b0d31ce
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 4 deletions.
23 changes: 21 additions & 2 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@
* [**hal config ci codebuild account get**](#hal-config-ci-codebuild-account-get)
* [**hal config ci codebuild account list**](#hal-config-ci-codebuild-account-list)
* [**hal config ci codebuild disable**](#hal-config-ci-codebuild-disable)
* [**hal config ci codebuild edit**](#hal-config-ci-codebuild-edit)
* [**hal config ci codebuild enable**](#hal-config-ci-codebuild-enable)
* [**hal config ci concourse**](#hal-config-ci-concourse)
* [**hal config ci concourse disable**](#hal-config-ci-concourse-disable)
Expand Down Expand Up @@ -3702,6 +3703,7 @@ hal config ci codebuild [parameters] [subcommands]
#### Subcommands
* `account`: Manage and view Spinnaker configuration for AWS CodeBuild service account.
* `disable`: Set the codebuild ci as disabled
* `edit`: Set CI provider-wide properties for AWS CodeBuild
* `enable`: Set the codebuild ci as enabled

---
Expand Down Expand Up @@ -3738,8 +3740,8 @@ hal config ci codebuild account add ACCOUNT [parameters]

#### Parameters
`ACCOUNT`: The name of the account to operate on.
* `--account-id`: (*Required*) The AWS account ID that will be used to trigger CodeBuild build.
* `--assume-role`: (*Required*) If set, Halyard will configure a credentials provider that uses AWS Security Token Service to assume the specified role.
* `--account-id`: The AWS account ID that will be used to trigger CodeBuild build.
* `--assume-role`: If set, Halyard will configure a credentials provider that uses AWS Security Token Service to assume the specified role.

Example: "user/spinnaker" or "role/spinnakerManaged"
* `--deployment`: If supplied, use this Halyard deployment. This will _not_ create a new deployment.
Expand Down Expand Up @@ -3830,6 +3832,23 @@ hal config ci codebuild disable [parameters]
* `--no-validate`: (*Default*: `false`) Skip validation.


---
## hal config ci codebuild edit

Set CI provider-wide properties for AWS CodeBuild

#### Usage
```
hal config ci codebuild edit [parameters]
```

#### Parameters
* `--access-key-id`: Your AWS Access Key ID. If not provided, Halyard/Spinnaker will try to find AWS credentials as described at [http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default](http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default)
* `--deployment`: If supplied, use this Halyard deployment. This will _not_ create a new deployment.
* `--no-validate`: (*Default*: `false`) Skip validation.
* `--secret-access-key`: (*Sensitive data* - user will be prompted on standard input) Your AWS Secret Key.


---
## hal config ci codebuild enable

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2020 Amazon.com, Inc.
*
* 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.netflix.spinnaker.halyard.cli.command.v1.config.ci;

import com.beust.jcommander.Parameters;
import com.netflix.spinnaker.halyard.cli.services.v1.Daemon;
import com.netflix.spinnaker.halyard.cli.services.v1.OperationHandler;
import com.netflix.spinnaker.halyard.cli.ui.v1.AnsiUi;
import com.netflix.spinnaker.halyard.config.model.v1.node.CIAccount;
import com.netflix.spinnaker.halyard.config.model.v1.node.Ci;
import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@Parameters(separators = "=")
@EqualsAndHashCode(callSuper = false)
public abstract class AbstractEditCiCommand<A extends CIAccount, C extends Ci<A>>
extends AbstractCiCommand {
String commandName = "edit";

@Override
protected void executeThis() {
String ciName = getCiName();
String currentDeployment = getCurrentDeployment();

Ci ci =
new OperationHandler<Ci>()
.setFailureMesssage("Failed to get ci provider " + ciName + ".")
.setOperation(Daemon.getCi(currentDeployment, ciName, false))
.get();

int originalHash = ci.hashCode();

ci = editCi((C) ci);

if (originalHash == ci.hashCode()) {
AnsiUi.failure("No changes supplied.");
return;
}

new OperationHandler<Void>()
.setFailureMesssage("Failed to edit ci provider " + ci + ".")
.setSuccessMessage("Successfully edited ci provider " + ci + ".")
.setOperation(Daemon.setCi(currentDeployment, ciName, !noValidate, ci))
.get();
}

protected abstract Ci editCi(C ci);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,11 @@ protected String getCiFullName() {

@Parameter(
names = "--account-id",
required = true,
description = AwsCodeBuildCommandProperties.ACCOUNT_ID_DESCRIPTION)
private String accountId;

@Parameter(
names = "--assume-role",
required = true,
description = AwsCodeBuildCommandProperties.ASSUME_ROLE_DESCRIPTION)
private String assumeRole;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public String getCommandName() {
public AwsCodeBuildCommand() {
super();
registerSubcommand(new AwsCodeBuildAccountCommand());
registerSubcommand(new AwsCodeBuildEditCiCommand());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ public class AwsCodeBuildCommandProperties {
"If set, Halyard will configure a credentials provider that uses AWS "
+ "Security Token Service to assume the specified role.\n\n"
+ "Example: \"user/spinnaker\" or \"role/spinnakerManaged\"";
static final String ACCESS_KEY_ID_DESCRIPTION =
"Your AWS Access Key ID. If not provided, Halyard/Spinnaker will try to find AWS credentials "
+ "as described at http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default";

static final String SECRET_KEY_DESCRIPTION = "Your AWS Secret Key.";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2020 Amazon.com, Inc.
*
* 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.netflix.spinnaker.halyard.cli.command.v1.config.ci.codebuild;

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.netflix.spinnaker.halyard.cli.command.v1.config.ci.AbstractEditCiCommand;
import com.netflix.spinnaker.halyard.config.model.v1.ci.codebuild.AwsCodeBuild;
import com.netflix.spinnaker.halyard.config.model.v1.ci.codebuild.AwsCodeBuildAccount;
import com.netflix.spinnaker.halyard.config.model.v1.node.Ci;
import lombok.Getter;

@Parameters(separators = "=")
public class AwsCodeBuildEditCiCommand
extends AbstractEditCiCommand<AwsCodeBuildAccount, AwsCodeBuild> {
protected String getCiName() {
return "codebuild";
}

@Override
protected String getCiFullName() {
return "AWS CodeBuild";
}

@Getter String shortDescription = "Set CI provider-wide properties for " + getCiFullName();

@Parameter(
names = "--access-key-id",
description = AwsCodeBuildCommandProperties.ACCESS_KEY_ID_DESCRIPTION)
private String accessKeyId;

@Parameter(
names = "--secret-access-key",
description = AwsCodeBuildCommandProperties.SECRET_KEY_DESCRIPTION,
password = true)
private String secretAccessKey;

@Override
protected Ci editCi(AwsCodeBuild ci) {
ci.setAccessKeyId(isSet(accessKeyId) ? accessKeyId : ci.getAccessKeyId());
ci.setSecretAccessKey(isSet(secretAccessKey) ? secretAccessKey : ci.getSecretAccessKey());
return ci;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,14 @@ public static Supplier<Ci> getCi(String deploymentName, String ciName, boolean v
};
}

public static Supplier<Void> setCi(
String deploymentName, String ciName, boolean validate, Ci ci) {
return () -> {
ResponseUnwrapper.get(getService().setCi(deploymentName, ciName, validate, ci));
return null;
};
}

public static Supplier<Void> setCiEnableDisable(
String deploymentName, String ciName, boolean validate, boolean enable) {
return () -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,13 @@ DaemonTask<Halconfig, Ci> getCi(
@Path("ciName") String ciName,
@Query("validate") boolean validate);

@PUT("/v1/config/deployments/{deploymentName}/ci/{ciName}/")
DaemonTask<Halconfig, Void> setCi(
@Path("deploymentName") String deploymentName,
@Path("ciName") String ciName,
@Query("validate") boolean validate,
@Body Ci ci);

@PUT("/v1/config/deployments/{deploymentName}/ci/{ciName}/enabled/")
DaemonTask<Halconfig, Void> setCiEnabled(
@Path("deploymentName") String deploymentName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
@EqualsAndHashCode(callSuper = false)
public class AwsCodeBuild extends Ci<AwsCodeBuildAccount> {
private boolean enabled;
private String accessKeyId;
private String secretAccessKey;
private List<AwsCodeBuildAccount> accounts = new ArrayList<>();

public List<AwsCodeBuildAccount> listAccounts() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import com.netflix.spinnaker.halyard.config.model.v1.ci.jenkins.JenkinsCi;
import com.netflix.spinnaker.halyard.config.model.v1.ci.travis.TravisCi;
import com.netflix.spinnaker.halyard.config.model.v1.ci.wercker.WerckerCi;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Optional;
import lombok.Data;
import lombok.EqualsAndHashCode;

Expand Down Expand Up @@ -53,4 +56,18 @@ public boolean ciEnabled() {
public String getNodeName() {
return "ci";
}

public static Class<? extends Ci> translateCiType(String ciName) {
Optional<? extends Class<?>> res =
Arrays.stream(Cis.class.getDeclaredFields())
.filter(f -> f.getName().equals(ciName))
.map(Field::getType)
.findFirst();

if (res.isPresent()) {
return (Class<? extends Ci>) res.get();
} else {
throw new IllegalArgumentException("No ci with name \"" + ciName + "\" handled by halyard");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.spinnaker.halyard.config.error.v1.ConfigNotFoundException;
import com.netflix.spinnaker.halyard.config.error.v1.IllegalConfigException;
import com.netflix.spinnaker.halyard.config.model.v1.ci.codebuild.AwsCodeBuild;
import com.netflix.spinnaker.halyard.config.model.v1.node.CIAccount;
import com.netflix.spinnaker.halyard.config.model.v1.node.Ci;
import com.netflix.spinnaker.halyard.config.model.v1.node.Cis;
import com.netflix.spinnaker.halyard.config.model.v1.node.DeploymentConfiguration;
import com.netflix.spinnaker.halyard.config.model.v1.node.NodeFilter;
import com.netflix.spinnaker.halyard.config.problem.v1.ConfigProblemBuilder;
import com.netflix.spinnaker.halyard.config.services.v1.DeploymentService;
import com.netflix.spinnaker.halyard.config.services.v1.LookupService;
import com.netflix.spinnaker.halyard.config.services.v1.ValidateService;
import com.netflix.spinnaker.halyard.core.error.v1.HalException;
Expand All @@ -41,18 +45,21 @@ public abstract class CiService<T extends CIAccount, U extends Ci<T>> {
protected final LookupService lookupService;
protected final ObjectMapper objectMapper;
private final ValidateService validateService;
private final DeploymentService deploymentService;

@Component
@RequiredArgsConstructor
public static class Members {
private final LookupService lookupService;
private final ValidateService validateService;
private final DeploymentService deploymentService;
private final ObjectMapper objectMapper = new ObjectMapper();
}

public CiService(Members members) {
this.lookupService = members.lookupService;
this.validateService = members.validateService;
this.deploymentService = members.deploymentService;
this.objectMapper = members.objectMapper;
}

Expand Down Expand Up @@ -89,6 +96,20 @@ public U getCi(String deploymentName) {
}
}

public void setCi(String deploymentName, Ci ci) {
DeploymentConfiguration deploymentConfiguration =
deploymentService.getDeploymentConfiguration(deploymentName);
Cis cis = deploymentConfiguration.getCi();
switch (ci.getNodeName()) {
case "codebuild":
cis.setCodebuild((AwsCodeBuild) ci);
break;
default:
throw new IllegalArgumentException(
"SetCi is not supported by ci provider " + ci.getNodeName());
}
}

public void setEnabled(String deploymentName, boolean enabled) {
Ci ci = getCi(deploymentName);
ci.setEnabled(enabled);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.netflix.spinnaker.halyard.config.config.v1.HalconfigParser;
import com.netflix.spinnaker.halyard.config.model.v1.node.CIAccount;
import com.netflix.spinnaker.halyard.config.model.v1.node.Ci;
import com.netflix.spinnaker.halyard.config.model.v1.node.Cis;
import com.netflix.spinnaker.halyard.config.model.v1.node.Halconfig;
import com.netflix.spinnaker.halyard.config.services.v1.ci.CiService;
import com.netflix.spinnaker.halyard.core.tasks.v1.DaemonTask;
Expand Down Expand Up @@ -65,6 +66,21 @@ DaemonTask<Halconfig, U> ci(
.execute(validationSettings);
}

@RequestMapping(value = "/", method = RequestMethod.PUT)
DaemonTask<Halconfig, Void> setCi(
@PathVariable String deploymentName,
@ModelAttribute ValidationSettings validationSettings,
@RequestBody Object rawCi) {
Ci ci = objectMapper.convertValue(rawCi, Cis.translateCiType(ciService.ciName()));
return GenericUpdateRequest.<Ci>builder(halconfigParser)
.stagePath(halconfigDirectoryStructure.getStagingPath(deploymentName))
.updater(c -> ciService.setCi(deploymentName, c))
.validator(() -> ciService.validateCi(deploymentName))
.description("Edit " + ciService.ciName() + " settings")
.build()
.execute(validationSettings, ci);
}

@RequestMapping(value = "/enabled", method = RequestMethod.PUT)
DaemonTask<Halconfig, Void> setEnabled(
@PathVariable String deploymentName,
Expand Down

0 comments on commit b0d31ce

Please sign in to comment.