Skip to content

Commit

Permalink
Reload configuration as code via curl instead of cli (#145)
Browse files Browse the repository at this point in the history
* Reload configuration as code via curl instead of cli

Signed-off-by: Sayali Gaikawad <[email protected]>
  • Loading branch information
gaiksaya authored Jun 20, 2022
1 parent 131de5a commit eeaa11b
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 12 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ OpenSearch Continuous Integration is an open source CI system for OpenSearch and

`npm run cdk deploy OpenSearch-CI-Config-Dev -- -c useSsl=false -c runWithOidc=false`

1. Locate the secret manager arns in the ci-config-stack outputs for `CASC_RELOAD_TOKEN` and update the secret value ([see docs](https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/put-secret-value.html)) with the password you want to use to reload jenkins configuration. _Do not enclose it in quotes_
```
$aws secretsmanager put-secret-value \
--secret-id MyCASCreloadTokenSecretARN \
--secret-string CascReloadToken
```
1. [Optional](#ssl-configuration) Configure the elements of the config stack for SSL configuration
1. [Optional](#setup-openid-connect-oidc-via-federate) Configure the elements setting up oidc via federate
1. Deploy the ci-stack, takes ~10 minutes to deploy (parameter values depend on step 2 and step 3)
Expand Down
10 changes: 10 additions & 0 deletions lib/ci-config-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export class CIConfigStack extends Stack {

static readonly OIDC_CONFIGURATION_VALUE_SECRET_EXPORT_VALUE: string = 'OIDCConfigValueSecret';

static readonly CASC_RELOAD_TOKEN_SECRET_EXPORT_VALUE: string = 'casc';

constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);

Expand All @@ -46,6 +48,9 @@ export class CIConfigStack extends Stack {
const OIDCConfigValuesSecret = new Secret(this, 'OIDCConfigValues', {
description: 'OIDC params in JSON format',
});
const CascReloadTokenValuesSecret = new Secret(this, 'CascReloadTokenValue', {
description: 'Reload token (password) required for configuration as code plugin',
});

new CfnOutput(this, 'certificateArnSecret', {
value: arnSecret.secretArn,
Expand Down Expand Up @@ -76,5 +81,10 @@ export class CIConfigStack extends Stack {
value: OIDCConfigValuesSecret.secretArn,
exportName: CIConfigStack.OIDC_CONFIGURATION_VALUE_SECRET_EXPORT_VALUE,
});

new CfnOutput(this, 'cascSecretValue', {
value: CascReloadTokenValuesSecret.secretArn,
exportName: CIConfigStack.CASC_RELOAD_TOKEN_SECRET_EXPORT_VALUE,
});
}
}
2 changes: 2 additions & 0 deletions lib/ci-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export class CIStack extends Stack {
const importedRedirectUrlSecretBucketValue = Fn.importValue(`${CIConfigStack.REDIRECT_URL_SECRET_EXPORT_VALUE}`);
const importedOidcConfigValuesSecretBucketValue = Fn.importValue(`${CIConfigStack.OIDC_CONFIGURATION_VALUE_SECRET_EXPORT_VALUE}`);
const certificateArn = Secret.fromSecretCompleteArn(this, 'certificateArn', importedArnSecretBucketValue.toString());
const importedReloadPasswordSecretsArn = Fn.importValue(`${CIConfigStack.CASC_RELOAD_TOKEN_SECRET_EXPORT_VALUE}`);
const listenerCertificate = ListenerCertificate.fromArn(certificateArn.secretValue.toString());
const agentNode = new AgentNodes(this);
const agentNodes: AgentNodeProps[] = [agentNode.AL2_X64, agentNode.AL2_X64_DOCKER_1, agentNode.AL2_ARM64, agentNode.AL2_ARM64_DOCKER_1,
Expand All @@ -109,6 +110,7 @@ export class CIStack extends Stack {
efsSG: securityGroups.efsSG,
dataRetention: props.dataRetention ?? false,
envVarsFilePath: props.envVarsFilePath ?? '',
reloadPasswordSecretsArn: importedReloadPasswordSecretsArn.toString(),
sslCertContentsArn: importedContentsSecretBucketValue.toString(),
sslCertChainArn: importedContentsChainBucketValue.toString(),
sslCertPrivateKeyContentsArn: importedCertSecretBucketValue.toString(),
Expand Down
21 changes: 12 additions & 9 deletions lib/compute/jenkins-main-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export interface JenkinsMainNodeProps extends HttpConfigProps, OidcFederateProps
readonly vpc: Vpc;
readonly sg: SecurityGroup;
readonly envVarsFilePath: string;
readonly reloadPasswordSecretsArn: string;
readonly failOnCloudInitError?: boolean;
}

Expand Down Expand Up @@ -144,6 +145,7 @@ export class JenkinsMainNode {
props,
props,
jenkinsyaml,
props.reloadPasswordSecretsArn,
this.EFS_ID,
)),
blockDevices: [{
Expand Down Expand Up @@ -222,7 +224,8 @@ export class JenkinsMainNode {
}

public static configElements(stackName: string, stackRegion: string, httpConfigProps: HttpConfigProps,
oidcFederateProps: OidcFederateProps, dataRetentionProps : DataRetentionProps, jenkinsyaml: string, efsId?: string): InitElement[] {
oidcFederateProps: OidcFederateProps, dataRetentionProps : DataRetentionProps, jenkinsyaml: string,
reloadPasswordSecretsArn: string, efsId?: string): InitElement[] {
return [
InitPackage.yum('wget'),
InitPackage.yum('openssl'),
Expand Down Expand Up @@ -365,15 +368,15 @@ export class JenkinsMainNode {
: 'echo Data rentention is disabled, not mounting efs'),

InitFile.fromFileInline('/docker-compose.yml', join(__dirname, '../../resources/docker-compose.yml')),
InitCommand.shellCommand('systemctl start docker && docker-compose up -d'),

InitCommand.shellCommand('systemctl start docker &&'
+ ` var=\`aws --region ${stackRegion} secretsmanager get-secret-value --secret-id ${reloadPasswordSecretsArn} --query SecretString --output text\` &&`
+ ' yq -i \'.services.jenkins.environment[1] = "CASC_RELOAD_TOKEN=\'$var\'"\' docker-compose.yml &&'
+ ' docker-compose up -d'),

// Commands are fired one after the other but it does not wait for the command to complete.
// Therefore, sleep 90 seconds to wait for jenkins to start
InitCommand.shellCommand('sleep 90'),

// Download jenkins-cli from the local machine
InitCommand.shellCommand('until $(curl --output /dev/null --silent --head --fail http://localhost:8080); do sleep 5; done &&'
+ ' wget -O "jenkins-cli.jar" http://localhost:8080/jnlpJars/jenkins-cli.jar'),
InitCommand.shellCommand('sleep 60'),

InitFile.fromFileInline('/initial_jenkins.yaml', jenkinsyaml),

Expand All @@ -390,8 +393,8 @@ export class JenkinsMainNode {

// Reload configuration via Jenkins.yaml
InitCommand.shellCommand('cp /initial_jenkins.yaml /var/lib/jenkins/jenkins.yaml &&'
+ ' java -jar /jenkins-cli.jar -s http://localhost:8080 reload-jcasc-configuration'),

+ ` var=\`aws --region ${stackRegion} secretsmanager get-secret-value --secret-id ${reloadPasswordSecretsArn} --query SecretString --output text\` &&`
+ ' curl -f -X POST "http://localhost:8080/reload-configuration-as-code/?casc-reload-token=$var"'),
];
}

Expand Down
3 changes: 2 additions & 1 deletion resources/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: '3.8'
services:
jenkins:
image: opensearchstaging/jenkins:2.332.3-lts-jdk8
restart: on-failure
restart: on-failure
privileged: true
tty: true
user: root
Expand All @@ -12,6 +12,7 @@ services:
container_name: jenkins
environment:
- JENKINS_JAVA_OPTS="-Xmx8g"
- CASC_RELOAD_TOKEN=reloadPasswordHere
volumes:
- /var/lib/jenkins:/var/jenkins_home
deploy:
Expand Down
5 changes: 3 additions & 2 deletions test/compute/jenkins-main-node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ describe('JenkinsMainNode Config Elements', () => {
runWithOidc: true,
}, {
dataRetention: true,
}, 'test/data/jenkins.yaml');
}, 'test/data/jenkins.yaml',
'ARN:ABC');

// THEN
test('Config elements expected counts', async () => {
expect(configElements.filter((e) => e.elementType === 'COMMAND')).toHaveLength(19);
expect(configElements.filter((e) => e.elementType === 'COMMAND')).toHaveLength(18);
expect(configElements.filter((e) => e.elementType === 'PACKAGE')).toHaveLength(10);
expect(configElements.filter((e) => e.elementType === 'FILE')).toHaveLength(4);
});
Expand Down

0 comments on commit eeaa11b

Please sign in to comment.