├── .travis.yml
├── renovate.json
├── src
├── test
│ ├── resources
│ │ ├── permissions_invalid_yaml.yaml
│ │ ├── deployment
│ │ │ ├── branch-service
│ │ │ │ ├── deployment.yaml
│ │ │ │ ├── deployment-included-tasks-missing-property.yaml
│ │ │ │ ├── deployment-included-tasks.yaml
│ │ │ │ ├── deployment-inline-tasks.yaml
│ │ │ │ └── deployment-combined.yaml
│ │ │ └── include
│ │ │ │ ├── cloudformation-deploy-tasks-missing-property.yaml
│ │ │ │ ├── cloudformation-deploy-tasks.yaml
│ │ │ │ ├── deployment-tasks.yaml
│ │ │ │ └── deployment-environments.yaml
│ │ └── au
│ │ │ └── com
│ │ │ └── reece
│ │ │ └── de
│ │ │ └── bamboospecs
│ │ │ └── illegalCharacters.yaml
│ └── java
│ │ └── au
│ │ └── com
│ │ └── reece
│ │ └── de
│ │ └── bamboospecs
│ │ ├── BuildControlTest.java
│ │ ├── ReeceSpecsTest.java
│ │ ├── DeploymentIncludedTasksTest.java
│ │ └── support
│ │ └── JUnitResultHelperTest.java
└── main
│ ├── resources
│ ├── resultTemplate.twig
│ └── log4j2.xml
│ └── java
│ └── au
│ └── com
│ └── reece
│ └── de
│ └── bamboospecs
│ ├── models
│ ├── enums
│ │ ├── InjectScopeType.java
│ │ ├── TriggerType.java
│ │ ├── TaskType.java
│ │ ├── PlanBranchCreateStrategy.java
│ │ ├── NotificationTrigger.java
│ │ └── SpecFileType.java
│ ├── docker
│ │ ├── PortMapping.java
│ │ ├── VolumeMapping.java
│ │ ├── DockerStartCheck.java
│ │ └── DockerContainer.java
│ ├── DomainModel.java
│ ├── deployment
│ │ ├── DeploymentPermissionsModel.java
│ │ ├── environment
│ │ │ ├── EnvironmentPermissionModel.java
│ │ │ ├── EnvironmentsModel.java
│ │ │ └── EnvironmentModel.java
│ │ ├── DeploymentPermissionModel.java
│ │ └── DeploymentModel.java
│ ├── RequirementModel.java
│ ├── DownloadArtifactModel.java
│ ├── ArtifactSubscriptionModel.java
│ ├── BambooYamlFileModel.java
│ ├── ArtifactModel.java
│ ├── StageModel.java
│ ├── PermissionFileModel.java
│ ├── ReleaseNamingModel.java
│ ├── IncludeEnvironmentsModel.java
│ ├── DependencyModel.java
│ ├── PermissionModel.java
│ ├── ProjectPermissionModel.java
│ ├── PlanBranchManagementModel.java
│ ├── TriggerModel.java
│ ├── NotificationModel.java
│ ├── RepositoryModel.java
│ ├── BuildModel.java
│ ├── StageJobModel.java
│ └── TaskModel.java
│ ├── validation
│ ├── IllegalCharacterValidator.java
│ └── NoIllegalCharacters.java
│ ├── NoOpController.java
│ ├── BambooController.java
│ ├── DeploymentControl.java
│ ├── PermissionsControl.java
│ ├── support
│ └── JUnitResultHelper.java
│ ├── BuildControl.java
│ └── ReeceSpecs.java
├── specifications
└── bamboo-spec-test-project
│ ├── permissions.yaml
│ ├── deployment.yaml
│ └── plan.yaml
├── .gitignore
├── pom.xml
├── LICENSE.txt
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json"
3 | }
4 |
--------------------------------------------------------------------------------
/src/test/resources/permissions_invalid_yaml.yaml:
--------------------------------------------------------------------------------
1 | this isn't valid yaml
2 | {}
3 | 00a
4 | -
5 | [
6 | !!!
7 |
--------------------------------------------------------------------------------
/specifications/bamboo-spec-test-project/permissions.yaml:
--------------------------------------------------------------------------------
1 | bambooServer: https://bamboo.reecenet.org/bamboo
2 | projects:
3 | - plans: [BST-ST]
4 | permissions:
5 | - groups: [Cyborg_Team]
6 | users: [islaa, tobind, visserp]
7 | grant: [VIEW, EDIT, BUILD, CLONE, ADMIN]
8 | deployments:
9 | - name: Bamboo Specs Testing
10 | permissions:
11 | - users: [dooleyj, poultonj]
12 | grant: [VIEW, EDIT]
13 | environments:
14 | - names: [Production (AU + NZ)]
15 | permissions:
16 | - groups: [Cyborg_Team]
17 | grant: [VIEW, EDIT, BUILD]
18 |
--------------------------------------------------------------------------------
/src/test/resources/deployment/branch-service/deployment.yaml:
--------------------------------------------------------------------------------
1 | specType: deployment
2 | bambooServer: https://bamboo.reecenet.org/bamboo
3 | name: Branch Service (development - K8S Specs)
4 | description: Deployment for Branch Service
5 | buildProject: BRANCHSHRDSRV
6 | buildPlan: BRANCHK8SDEV
7 | releaseNaming:
8 | pattern: 0.0.1-${bamboo.buildNumber}
9 | variables:
10 | application_name: branch-service
11 | includeEnvironments:
12 | from: ../include/deployment-environments.yaml
13 | environments: [
14 | trstst03,
15 | trstst04,
16 | trstst05
17 | ]
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | results/**
2 | .credentials
3 |
4 | ### Maven
5 | target/
6 | pom.xml.tag
7 | pom.xml.releaseBackup
8 | pom.xml.versionsBackup
9 | pom.xml.next
10 | release.properties
11 | dependency-reduced-pom.xml
12 | buildNumber.properties
13 |
14 | ### Java
15 | # Compiled class file
16 | *.class
17 |
18 | # Log file
19 | *.log
20 |
21 | # Package Files #
22 | *.jar
23 |
24 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
25 | hs_err_pid*
26 |
27 | ### Idea
28 | .idea/
29 | *.iml
30 |
31 | ### jenv
32 | .java-version
33 |
34 | ### others
35 | bin
36 | .settings
37 | .classpath
38 | .project
39 | .factorypath
--------------------------------------------------------------------------------
/src/main/resources/resultTemplate.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% if (successful) %}
4 |
5 | {% else %}
6 |
7 | {% autoescape 'html' %}
8 |
10 | {% for line in stacktrace %}
11 | {{ line }}
12 | {% endfor %}
13 |
14 | {% endautoescape %}
15 |
16 | {% endif %}
17 |
18 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/enums/InjectScopeType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.enums;
18 |
19 | public enum InjectScopeType {
20 | LOCAL, RESULT
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/enums/TriggerType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.enums;
18 |
19 | public enum TriggerType {
20 | AFTER_STASH_COMMIT, AFTER_SUCCESSFUL_BUILD_PLAN, SCHEDULED
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/enums/TaskType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.enums;
18 |
19 | public enum TaskType {
20 | VCS, SCRIPT, DOCKER, CLEAN, ARTEFACT, SPECIFIC_ARTEFACTS, INJECT, JUNIT, TESTNG, ARTIFACTORY, CUCUMBER_REPORT
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/enums/PlanBranchCreateStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.enums;
18 |
19 | public enum PlanBranchCreateStrategy {
20 | MANUALLY,
21 | ON_PULL_REQUEST,
22 | ON_NEW_BRANCH,
23 | ON_BRANCH_PATTERN
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/docker/PortMapping.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.docker;
18 |
19 | import javax.validation.constraints.NotNull;
20 |
21 | public class PortMapping {
22 | @NotNull
23 | public Integer local;
24 |
25 | @NotNull
26 | public Integer container;
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/enums/NotificationTrigger.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.enums;
18 |
19 | public enum NotificationTrigger {
20 | PLAN_COMPLETED,
21 | PLAN_FAILED,
22 | STATUS_CHANGED,
23 | DEPLOYMENT_FAILED,
24 | DEPLOYMENT_FINISHED,
25 | DEPLOYMENT_STARTED_AND_FINISHED
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/docker/VolumeMapping.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.docker;
18 |
19 | import javax.validation.constraints.NotEmpty;
20 | import javax.validation.constraints.NotNull;
21 |
22 | public class VolumeMapping {
23 | @NotNull
24 | @NotEmpty
25 | public String local;
26 |
27 | @NotNull
28 | @NotEmpty
29 | public String container;
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/DomainModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import org.apache.commons.lang3.builder.ToStringBuilder;
20 | import org.apache.commons.lang3.builder.ToStringStyle;
21 |
22 | abstract class DomainModel {
23 | @Override
24 | public String toString() {
25 | return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/deployment/DeploymentPermissionsModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.deployment;
18 |
19 | import au.com.reece.de.bamboospecs.models.PermissionModel;
20 |
21 | import javax.validation.constraints.NotNull;
22 |
23 | public class DeploymentPermissionsModel {
24 | @NotNull
25 | public PermissionModel project;
26 |
27 | @NotNull
28 | public PermissionModel environment;
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/resources/deployment/branch-service/deployment-included-tasks-missing-property.yaml:
--------------------------------------------------------------------------------
1 | specType: deployment
2 | bambooServer: https://bamboo.reecenet.org/bamboo
3 | name: Cloudformation Deployment - Branch Service
4 | description: Deployment Plan for Branch Service Cloudformation Templates
5 | buildProject: BRANCHSHRDSRV
6 | buildPlan: BRANCHCFBUILD
7 | releaseNaming:
8 | pattern: 1.0.${bamboo.buildNumber}
9 | environments:
10 | - environment: trstst02
11 | description: Product & Pricing Test AU
12 | requirements:
13 | - name: pegasus
14 | notifications:
15 | - when: DEPLOYMENT_FAILED
16 | slack: https://hooks.slack.com/services/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
17 | variables:
18 | application_name: branch-service
19 | k8s_cluster: non-production-internal-cluster
20 | aws_account: core-nonprod
21 | app_support_environment: nonprod
22 | stack_environment: test
23 | stack_namespace: trstst02
24 | stack_cost_centre: 3963
25 | stack_owner: Inventory
26 | triggers:
27 | includedTasks: ../include/cloudformation-deploy-tasks-missing-property.yaml
28 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/docker/DockerStartCheck.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.docker;
18 |
19 | import com.atlassian.bamboo.specs.builders.task.DockerRunContainerTask;
20 |
21 | public class DockerStartCheck {
22 | public String url;
23 |
24 | public int timeout = 120;
25 |
26 | public void applyConfig(DockerRunContainerTask docker) {
27 | docker.waitToStart(true).serviceURLPattern(this.url).serviceTimeoutInSeconds(this.timeout);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/RequirementModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.requirement.Requirement;
20 |
21 | import javax.validation.constraints.NotEmpty;
22 | import javax.validation.constraints.NotNull;
23 |
24 | public class RequirementModel {
25 | @NotNull
26 | @NotEmpty
27 | public String name;
28 |
29 | // add later: matchType, matchValue, etc.
30 |
31 | public Requirement asRequirement() {
32 | return new Requirement(this.name);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/validation/IllegalCharacterValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.validation;
18 |
19 | import javax.validation.ConstraintValidator;
20 | import javax.validation.ConstraintValidatorContext;
21 |
22 | import static com.atlassian.bamboo.specs.api.validators.common.BambooStringUtils.containsRelaxedXssRelatedCharacters;
23 |
24 | public class IllegalCharacterValidator implements ConstraintValidator {
25 | public boolean isValid(String obj, ConstraintValidatorContext context) {
26 | return !containsRelaxedXssRelatedCharacters(obj);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/DownloadArtifactModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.builders.task.DownloadItem;
20 |
21 | import javax.validation.constraints.NotEmpty;
22 | import javax.validation.constraints.NotNull;
23 |
24 | public class DownloadArtifactModel extends DomainModel {
25 |
26 | @NotNull
27 | @NotEmpty
28 | public String name;
29 |
30 | @NotNull
31 | @NotEmpty
32 | public String path;
33 |
34 | public DownloadItem asDownloadItem() {
35 | return new DownloadItem().artifact(name).path(path);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/resources/au/com/reece/de/bamboospecs/illegalCharacters.yaml:
--------------------------------------------------------------------------------
1 | specType: build
2 | bambooServer: https://bamboo.reecenet.org/bamboo
3 | projectKey: DE
4 | projectName: Delivery Engineering
5 | planKey: JAYPHELLO
6 | labels:
7 | - Delivery-Engineering
8 | planName: jayp - Hello World
9 | description: build & test jayp hello world application
10 | repositories:
11 | - name: reece.tech
12 | projectKey: jayp
13 | repositorySlug: jayp-app
14 | branch: master
15 | branchManagement:
16 | createStrategy: ON_NEW_BRANCH
17 | variables:
18 | triggers:
19 | - type: AFTER_STASH_COMMIT
20 | description: Trigger from Stash changes
21 | notifications:
22 | - when: PLAN_COMPLETED
23 | recipientUsers: [jayp]
24 | stages:
25 | - name: Build test image
26 | jobs:
27 | - name: Build test image
28 | description: Build test image
29 | key: BUILDTEST
30 | requirements: []
31 | tasks:
32 | - type: CLEAN
33 | description: Clean working directory
34 | - type: VCS
35 | description: Git Checkout
36 | defaultRepository: true
37 | - type: SCRIPT
38 | description: Build test image
39 | body: |
40 | docker build -t jayp-testing:deleteme --target testing .
41 |
--------------------------------------------------------------------------------
/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/ArtifactSubscriptionModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.plan.artifact.ArtifactSubscription;
20 |
21 | import javax.validation.constraints.NotEmpty;
22 | import javax.validation.constraints.NotNull;
23 |
24 | public class ArtifactSubscriptionModel {
25 | @NotNull
26 | @NotEmpty
27 | public String name;
28 |
29 | @NotNull
30 | @NotEmpty
31 | public String destination;
32 |
33 | public ArtifactSubscription asArtifactSubscription() {
34 | return new ArtifactSubscription().artifact(this.name).destination(this.destination);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/BambooYamlFileModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import au.com.reece.de.bamboospecs.models.enums.SpecFileType;
20 | import com.fasterxml.jackson.annotation.JsonIgnore;
21 |
22 | import javax.validation.constraints.NotEmpty;
23 | import javax.validation.constraints.NotNull;
24 |
25 | public class BambooYamlFileModel extends DomainModel {
26 | @NotNull
27 | @NotEmpty
28 | public String specType;
29 |
30 | @NotNull
31 | @NotEmpty
32 | public String bambooServer;
33 |
34 | @JsonIgnore
35 | public SpecFileType getFileType() {
36 | return SpecFileType.fromString(this.specType);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/au/com/reece/de/bamboospecs/BuildControlTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs;
18 |
19 | import com.atlassian.bamboo.specs.util.SimpleUserPasswordCredentials;
20 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
21 | import org.junit.Test;
22 |
23 | import java.io.File;
24 |
25 | public class BuildControlTest {
26 | @Test(expected = RuntimeException.class)
27 | public void run() {
28 | BuildControl control = new BuildControl();
29 | UserPasswordCredentials users = new SimpleUserPasswordCredentials("xx", "xx");
30 | File file = new File(getClass().getResource("illegalCharacters.yaml").getPath());
31 | control.run(users, file, false);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/ArtifactModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.plan.artifact.Artifact;
20 |
21 | import javax.validation.constraints.NotEmpty;
22 | import javax.validation.constraints.NotNull;
23 |
24 | public class ArtifactModel extends DomainModel {
25 | @NotNull
26 | @NotEmpty
27 | public String name;
28 |
29 | @NotNull
30 | @NotEmpty
31 | public String pattern;
32 |
33 | @NotNull
34 | @NotEmpty
35 | public String location;
36 |
37 | public Boolean shared = false;
38 |
39 | public Artifact asArtifact() {
40 | return new Artifact(this.name).copyPattern(this.pattern).location(this.location).shared(this.shared);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/NoOpController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs;
18 |
19 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 |
23 | import java.io.File;
24 |
25 | public class NoOpController extends BambooController {
26 | private static final Logger LOGGER = LoggerFactory.getLogger(NoOpController.class);
27 |
28 | @Override
29 | public void run(UserPasswordCredentials adminUser, String filePath, boolean publish) {
30 | LOGGER.info("File {} is a an include - not processing", filePath);
31 | }
32 |
33 | @Override
34 | public void run(UserPasswordCredentials adminUser, File yamlFile, boolean publish) {
35 | run(adminUser, yamlFile.getPath(), publish);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/specifications/bamboo-spec-test-project/deployment.yaml:
--------------------------------------------------------------------------------
1 | bambooServer: https://bamboo.reecenet.org/bamboo
2 | name: Bamboo Specs Testing
3 | buildProject: BST
4 | buildPlan: ST
5 | description: Testing the Specs!
6 | releaseNaming:
7 | pattern: ${bamboo.version_major_number}.${bamboo.buildNumber}
8 | environments:
9 | - environment: Production (AU + NZ)
10 | description: Deployment plan for the Diary Notes Python Shared Service to production
11 | requirements:
12 | - name: system.docker.executable
13 | variables:
14 | deployment_script: cutover.py
15 | tasks:
16 | - type: VCS
17 | description: Running Man
18 | repositories:
19 | - name: Running Man
20 | - name: Running Man Properties
21 | path: properties
22 | - type: SCRIPT
23 | description: Cutover Blue to Green - Training
24 | body: |
25 | exit 1;
26 | ${deployment_script} ${bamboo.target_name} training_nz ${bamboo.version_major_number}.${bamboo.buildNumber}
27 | ${deployment_script} ${bamboo.target_name} training_au ${bamboo.version_major_number}.${bamboo.buildNumber}
28 | - type: SCRIPT
29 | description: Cutover Blue to Green - Production
30 | body: |
31 | exit 1;
32 | ${deployment_script} ${bamboo.target_name} prod_au ${bamboo.version_major_number}.${bamboo.buildNumber}
33 | ${deployment_script} ${bamboo.target_name} prod_nz ${bamboo.version_major_number}.${bamboo.buildNumber}
34 | triggers:
35 | - type: AFTER_SUCCESSFUL_BUILD_PLAN
36 | description: Deploy main plan branch (master)
37 |
--------------------------------------------------------------------------------
/specifications/bamboo-spec-test-project/plan.yaml:
--------------------------------------------------------------------------------
1 | bambooServer: https://bamboo.reecenet.org/bamboo
2 | specType: build
3 | projectKey: BST
4 | projectName: Bamboo Spec Testing
5 | planKey: ST
6 | planName: Spec Testing
7 | labels:
8 | - testing
9 | - label
10 | description: This is a test plan for bamboo specs
11 | linkedRepositories: [Bamboo Specs]
12 | notifications:
13 | - when: PLAN_COMPLETED
14 | recipientUsers: [patersoa]
15 | branchManagement:
16 | createStrategy: ON_NEW_BRANCH
17 | issueLinkingEnabled: false
18 | delayCleanAfterDelete: 2
19 | delayCleanAfterInactivity: 60
20 | triggers:
21 | - type: AFTER_STASH_COMMIT
22 | description: Trigger from stash changes
23 | stages:
24 | - name: Default Stage
25 | jobs:
26 | - name: Run Tests
27 | key: JOB1
28 | description: Run Python Unit Tests
29 | requirements:
30 | - name: system.docker.executable
31 | - name: DOCKER
32 | - name: LINUX
33 | artifacts:
34 | - name: PACT Contracts
35 | pattern: "**"
36 | location: pacts
37 | - name: Coverage Report
38 | pattern: "**"
39 | location: htmlcov
40 | tasks:
41 | - type: VCS
42 | description: Checkout Default Repository
43 | cleanCheckout: true
44 | defaultRepository: true
45 | - type: SCRIPT
46 | description: Build docker image
47 | body: |
48 | set -ex
49 | scripts/test_image.sh bamboo/${bamboo.target_name}
50 | - type: SCRIPT
51 | description: Run tests
52 | body: |
53 | set -ex
54 | scripts/run_tests.sh
55 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/validation/NoIllegalCharacters.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.validation;
18 |
19 | import javax.validation.Constraint;
20 | import javax.validation.ConstraintValidator;
21 | import javax.validation.Payload;
22 | import java.lang.annotation.Annotation;
23 | import java.lang.annotation.Retention;
24 | import java.lang.annotation.Target;
25 |
26 | import static java.lang.annotation.ElementType.*;
27 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
28 |
29 | @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
30 | @Retention(RUNTIME)
31 | @Constraint(validatedBy = IllegalCharacterValidator.class)
32 | public @interface NoIllegalCharacters {
33 | String message() default "field must not contain the characters \", &, ', <, >, \\";
34 |
35 | Class>[] groups() default { };
36 |
37 | Class extends Payload>[] payload() default { };
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/enums/SpecFileType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.enums;
18 |
19 | import javax.validation.constraints.NotNull;
20 |
21 | public enum SpecFileType {
22 | DEPLOYMENT("deployment"),
23 | DEPLOY_INCLUDE("deployInclude"),
24 | BUILD("build"),
25 | BUILD_INCLUDE("buildInclude"),
26 | PERMISSIONS("permissions");
27 |
28 | private final String fileTypeValue;
29 |
30 | SpecFileType(String fileTypeValue) {
31 | this.fileTypeValue = fileTypeValue;
32 | }
33 |
34 | public static SpecFileType fromString(@NotNull String type) {
35 | for (SpecFileType specFileType : SpecFileType.values()) {
36 | if (specFileType.fileTypeValue.equalsIgnoreCase(type)) {
37 | return specFileType;
38 | }
39 | }
40 | throw new IllegalArgumentException("The specType of spec file, " + type + ", is unknown");
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/docker/DockerContainer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.docker;
18 |
19 | import com.atlassian.bamboo.specs.builders.task.DockerRunContainerTask;
20 |
21 | public class DockerContainer {
22 | public String name;
23 |
24 | public String command;
25 |
26 | public String workingDirectory;
27 |
28 | public String environmentVariables;
29 |
30 | public void applyConfig(DockerRunContainerTask docker) {
31 | if (this.name != null) docker.containerName(this.name);
32 |
33 | if (this.command != null) docker.containerCommand(this.command);
34 |
35 | if (this.workingDirectory != null) {
36 | docker.containerWorkingDirectory(this.workingDirectory);
37 | } else {
38 | docker.containerWorkingDirectory("");
39 | }
40 |
41 | if (this.environmentVariables != null) docker.containerEnvironmentVariables(this.environmentVariables);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/StageModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.plan.Job;
20 | import com.atlassian.bamboo.specs.api.builders.plan.Stage;
21 |
22 | import javax.validation.Valid;
23 | import javax.validation.constraints.NotEmpty;
24 | import javax.validation.constraints.NotNull;
25 | import java.util.List;
26 | import java.util.stream.Collectors;
27 |
28 | public class StageModel extends DomainModel {
29 | public String yamlPath;
30 |
31 | @NotNull
32 | @NotEmpty
33 | public String name;
34 |
35 | @NotNull
36 | public List<@Valid StageJobModel> jobs;
37 |
38 | public String include;
39 |
40 | public Stage asStage() {
41 | Stage stage = new Stage(this.name);
42 | this.jobs.forEach(x -> x.yamlPath = this.yamlPath);
43 | return stage.jobs(jobs.stream().map(StageJobModel::asJob).collect(Collectors.toList()).toArray(new Job[]{}));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/PermissionFileModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import au.com.reece.de.bamboospecs.models.deployment.DeploymentPermissionModel;
20 | import com.atlassian.bamboo.specs.util.BambooServer;
21 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
22 |
23 | import javax.validation.Valid;
24 | import javax.validation.constraints.NotEmpty;
25 | import javax.validation.constraints.NotNull;
26 | import java.util.Set;
27 |
28 | public class PermissionFileModel extends BambooYamlFileModel {
29 | @NotNull
30 | @NotEmpty
31 | public Set<@Valid ProjectPermissionModel> projects;
32 |
33 | public Set<@Valid DeploymentPermissionModel> deployments;
34 |
35 | public void publish(BambooServer bambooServer, UserPasswordCredentials adminUser) {
36 | this.projects.forEach(x -> x.publishPermissions(bambooServer, adminUser));
37 | if (this.deployments != null) {
38 | this.deployments.forEach(x -> x.publishPermissions(bambooServer, adminUser));
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/ReleaseNamingModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.deployment.ReleaseNaming;
20 |
21 | import javax.validation.constraints.NotEmpty;
22 | import javax.validation.constraints.NotNull;
23 |
24 |
25 | // TODO document me
26 | public class ReleaseNamingModel {
27 | @NotNull
28 | @NotEmpty
29 | public String pattern;
30 |
31 | public boolean autoIncrement = false;
32 |
33 | public boolean retainNamingStrategyForBranches = false;
34 |
35 | public String[] autoIncrementVariables;
36 |
37 | public ReleaseNaming asReleaseNaming() {
38 | ReleaseNaming releaseNaming = new ReleaseNaming(this.pattern);
39 | releaseNaming = releaseNaming.autoIncrement(this.autoIncrement);
40 | releaseNaming.applicableToBranches(retainNamingStrategyForBranches);
41 |
42 | if (this.autoIncrementVariables != null) {
43 | releaseNaming = releaseNaming.variablesToAutoIncrement(this.autoIncrementVariables);
44 | }
45 | return releaseNaming;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/test/resources/deployment/include/cloudformation-deploy-tasks-missing-property.yaml:
--------------------------------------------------------------------------------
1 | - type: CLEAN
2 | description: Clean working directory
3 | - type: ARTEFACT
4 | description: Download Cloudformation Templates
5 | - type: VCS
6 | description: Git Checkout
7 | repositories:
8 | - name: oyster-scripts
9 | path: oyster-scripts
10 | - type: SCRIPT
11 | body: |
12 | echo "Unzip cloudformation templates"
13 | unzip cloudformation.zip
14 |
15 | echo "INFO: Getting credentials for '${bamboo.aws_account}' account"
16 | source aws-get-creds ${bamboo_aws_account}
17 |
18 | echo "INFO: Ensure '${bamboo_application_name}-${bamboo_stack_namespace}' is created and up-to-date"
19 | docker pull -q artifactory.reecenet.org:6555/utilities/hermitcrab
20 | docker run --rm --user "$(id -u):$(id -g)" --env-file "${AWSACCESSTEMP}" \
21 | --volume "$(pwd)/cloudformation/${bamboo_application_name}.yaml:/cfn/stack.template" \
22 | artifactory.reecenet.org:6555/utilities/hermitcrab \
23 | ${bamboo_application_name}-${bamboo_stack_namespace} up \
24 | --template /cfn/stack.template \
25 | --tags-input "{Name: ${bamboo_application_name}-${bamboo_stack_namespace}, CostCentre: ${bamboo_stack_cost_centre}, Environment: ${bamboo_stack_environment}, Owner: ${bamboo_stack_owner}}" \
26 | --override Namespace=${bamboo_stack_namespace} \
27 | --override AppSupportEnvironment=${bamboo_app_support_environment}
28 | - type: SCRIPT
29 | description: Rotate AWS API Keys
30 | body: |
31 | echo "Rotate AWS API Keys"
32 | set -u
33 | oyster-scripts/tools/aws-api-key-rotation.sh \
34 | -a "${bamboo_aws_account}" \
35 | -u "${bamboo_application_name}-${bamboo_stack_namespace}" \
36 | -c "${bamboo_k8s_cluster}" \
37 | -n "${bamboo_stack_namespace}"
38 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/IncludeEnvironmentsModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import au.com.reece.de.bamboospecs.models.deployment.environment.EnvironmentModel;
20 | import au.com.reece.de.bamboospecs.models.deployment.environment.EnvironmentsModel;
21 |
22 | import javax.validation.constraints.NotBlank;
23 | import javax.validation.constraints.NotNull;
24 | import java.nio.file.Path;
25 | import java.nio.file.Paths;
26 | import java.util.ArrayList;
27 |
28 | public class IncludeEnvironmentsModel {
29 |
30 | @NotNull
31 | @NotBlank
32 | public String from;
33 |
34 | @NotNull
35 | public String[] environments;
36 |
37 | public void addEnvironments(ArrayList environments, String yamlPath) {
38 | Path includedYaml = Paths.get(yamlPath, this.from);
39 | EnvironmentsModel included = EnvironmentsModel.readYAML(includedYaml.toString());
40 | for (String name : this.environments) {
41 | EnvironmentModel environmentModel = included.get(name);
42 | environmentModel.yamlPath = yamlPath;
43 | environments.add(environmentModel);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/resources/deployment/include/cloudformation-deploy-tasks.yaml:
--------------------------------------------------------------------------------
1 | - type: CLEAN
2 | description: Clean working directory
3 | - type: ARTEFACT
4 | description: Download Cloudformation Templates
5 | - type: VCS
6 | description: Git Checkout
7 | repositories:
8 | - name: oyster-scripts
9 | path: oyster-scripts
10 | - type: SCRIPT
11 | description: Install cloudformation templates to AWS
12 | body: |
13 | echo "Unzip cloudformation templates"
14 | unzip cloudformation.zip
15 |
16 | echo "INFO: Getting credentials for '${bamboo.aws_account}' account"
17 | source aws-get-creds ${bamboo_aws_account}
18 |
19 | echo "INFO: Ensure '${bamboo_application_name}-${bamboo_stack_namespace}' is created and up-to-date"
20 | docker pull -q artifactory.reecenet.org:6555/utilities/hermitcrab
21 | docker run --rm --user "$(id -u):$(id -g)" --env-file "${AWSACCESSTEMP}" \
22 | --volume "$(pwd)/cloudformation/${bamboo_application_name}.yaml:/cfn/stack.template" \
23 | artifactory.reecenet.org:6555/utilities/hermitcrab \
24 | ${bamboo_application_name}-${bamboo_stack_namespace} up \
25 | --template /cfn/stack.template \
26 | --tags-input "{Name: ${bamboo_application_name}-${bamboo_stack_namespace}, CostCentre: ${bamboo_stack_cost_centre}, Environment: ${bamboo_stack_environment}, Owner: ${bamboo_stack_owner}}" \
27 | --override Namespace=${bamboo_stack_namespace} \
28 | --override AppSupportEnvironment=${bamboo_app_support_environment}
29 | - type: SCRIPT
30 | description: Rotate AWS API Keys
31 | body: |
32 | echo "Rotate AWS API Keys"
33 | set -u
34 | oyster-scripts/tools/aws-api-key-rotation.sh \
35 | -a "${bamboo_aws_account}" \
36 | -u "${bamboo_application_name}-${bamboo_stack_namespace}" \
37 | -c "${bamboo_k8s_cluster}" \
38 | -n "${bamboo_stack_namespace}"
39 |
--------------------------------------------------------------------------------
/src/test/resources/deployment/branch-service/deployment-included-tasks.yaml:
--------------------------------------------------------------------------------
1 | specType: deployment
2 | bambooServer: https://bamboo.reecenet.org/bamboo
3 | name: Cloudformation Deployment - Branch Service
4 | description: Deployment Plan for Branch Service Cloudformation Templates
5 | buildProject: BRANCHSHRDSRV
6 | buildPlan: BRANCHCFBUILD
7 | releaseNaming:
8 | pattern: 1.0.${bamboo.buildNumber}
9 | environments:
10 | - environment: trstst02
11 | description: Product & Pricing Test AU
12 | requirements:
13 | - name: pegasus
14 | notifications:
15 | - when: DEPLOYMENT_FAILED
16 | slack: https://hooks.slack.com/services/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
17 | variables:
18 | application_name: branch-service
19 | k8s_cluster: non-production-internal-cluster
20 | aws_account: core-nonprod
21 | app_support_environment: nonprod
22 | stack_environment: test
23 | stack_namespace: trstst02
24 | stack_cost_centre: 3963
25 | stack_owner: Inventory
26 | triggers:
27 | includedTasks: ../include/cloudformation-deploy-tasks.yaml
28 |
29 | - environment: trstst04
30 | description: Product & Pricing Test AU
31 | requirements:
32 | - name: pegasus
33 | notifications:
34 | - when: DEPLOYMENT_FAILED
35 | slack: https://hooks.slack.com/services/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
36 | variables:
37 | application_name: branch-service
38 | k8s_cluster: non-production-internal-cluster
39 | aws_account: core-nonprod
40 | app_support_environment: nonprod
41 | stack_environment: test
42 | stack_namespace: trstst04
43 | stack_cost_centre: 3963
44 | stack_owner: Inventory
45 | triggers:
46 | includedTasks: ../include/cloudformation-deploy-tasks.yaml
47 | tasks:
48 | - type: CLEAN
49 | description: Clean working directory
50 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/BambooController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs;
18 |
19 | import au.com.reece.de.bamboospecs.models.BambooYamlFileModel;
20 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
21 |
22 | import java.io.File;
23 |
24 | abstract class BambooController {
25 | static BambooController getBambooController(String path, BambooYamlFileModel bambooFile) {
26 | BambooController controller;
27 | switch (bambooFile.getFileType()) {
28 | case BUILD:
29 | controller = new BuildControl();
30 | break;
31 | case DEPLOYMENT:
32 | controller = new DeploymentControl();
33 | break;
34 | case PERMISSIONS:
35 | controller = new PermissionsControl();
36 | break;
37 | case BUILD_INCLUDE:
38 | case DEPLOY_INCLUDE:
39 | controller = new NoOpController();
40 | break;
41 | default:
42 | throw new RuntimeException(String.format("File %s is unknown (%s) - not processing", path, bambooFile.getFileType()));
43 | }
44 |
45 | return controller;
46 | }
47 |
48 | abstract void run(UserPasswordCredentials adminUser, String filePath, boolean publish);
49 |
50 | abstract void run(UserPasswordCredentials adminUser, File yamlFile, boolean publish);
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/au/com/reece/de/bamboospecs/ReeceSpecsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs;
18 |
19 | import au.com.reece.de.bamboospecs.support.JUnitResultHelper;
20 | import com.atlassian.bamboo.specs.util.SimpleUserPasswordCredentials;
21 | import org.junit.Before;
22 | import org.junit.Rule;
23 | import org.junit.Test;
24 | import org.junit.rules.ExpectedException;
25 | import org.mockito.Mock;
26 |
27 | import static org.mockito.ArgumentMatchers.any;
28 | import static org.mockito.ArgumentMatchers.anyLong;
29 | import static org.mockito.ArgumentMatchers.eq;
30 | import static org.mockito.Mockito.verify;
31 | import static org.mockito.MockitoAnnotations.initMocks;
32 |
33 | public class ReeceSpecsTest {
34 |
35 | @Rule
36 | public ExpectedException expectedException = ExpectedException.none();
37 |
38 | @Mock
39 | private JUnitResultHelper resultHelper;
40 |
41 | private ReeceSpecs testInstance;
42 |
43 | @Before
44 | public void setupTest() {
45 | initMocks(this);
46 |
47 | testInstance = new ReeceSpecs(resultHelper);
48 | }
49 |
50 | @Test
51 | public void runFileProcess_invalidFile() {
52 | testInstance.runFileProcess(new SimpleUserPasswordCredentials("user", "pass"), true, "classpath:/permissions_invalid_yaml.yaml");
53 |
54 | verify(resultHelper).handleOutcome(any(RuntimeException.class), anyLong(), eq("classpath:/permissions_invalid_yaml.yaml"));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/DependencyModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.plan.Plan;
20 | import com.atlassian.bamboo.specs.api.builders.plan.PlanIdentifier;
21 | import com.atlassian.bamboo.specs.api.builders.plan.dependencies.Dependencies;
22 | import com.atlassian.bamboo.specs.api.builders.plan.dependencies.DependenciesConfiguration;
23 |
24 | import java.util.ArrayList;
25 | import java.util.Set;
26 |
27 | public class DependencyModel extends DomainModel {
28 | public boolean requiresPassing = true;
29 |
30 | public Set plans;
31 |
32 | public boolean none = false;
33 |
34 | public void addToPlan(Plan plan) {
35 | ArrayList children = new ArrayList<>();
36 | if (this.none) {
37 | plan.dependencies(new Dependencies());
38 | return;
39 | }
40 |
41 | if (this.plans == null) {
42 | throw new RuntimeException("dependencies must be either 'none' or must list plans");
43 | }
44 |
45 | for (String idString : this.plans) {
46 | String[] parts = idString.split("-");
47 | children.add(new PlanIdentifier(parts[0], parts[1]));
48 | }
49 | plan.dependencies(new Dependencies()
50 | .configuration(new DependenciesConfiguration()
51 | .requireAllStagesPassing(this.requiresPassing))
52 | .childPlans(children.toArray(new PlanIdentifier[children.size()])));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/PermissionModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.permission.PermissionType;
20 | import com.atlassian.bamboo.specs.api.builders.permission.Permissions;
21 |
22 | import javax.validation.constraints.NotEmpty;
23 | import javax.validation.constraints.NotNull;
24 | import java.util.HashSet;
25 | import java.util.Set;
26 |
27 | public class PermissionModel extends DomainModel {
28 | public Set groups;
29 | public Set users;
30 |
31 | public Set getUsers() {
32 | return users == null ? new HashSet<>() : users;
33 | }
34 |
35 | public Set getGroups() {
36 | return groups == null ? new HashSet<>() : groups;
37 | }
38 |
39 | public boolean allLoggedInUsers = false;
40 |
41 | @NotNull
42 | @NotEmpty
43 | public Set grant;
44 |
45 | public void addToPermissions(Permissions permissions) {
46 | PermissionType[] permissionArray = this.grant.toArray(new PermissionType[this.grant.size()]);
47 |
48 | // Set user grant first
49 | for (String user : this.getUsers()) {
50 | permissions.userPermissions(user, permissionArray);
51 | }
52 |
53 | for (String group : this.getGroups()) {
54 | permissions.groupPermissions(group, permissionArray);
55 | }
56 |
57 | if (this.allLoggedInUsers) {
58 | permissions.loggedInUserPermissions(permissionArray);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/ProjectPermissionModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.permission.PermissionType;
20 | import com.atlassian.bamboo.specs.api.builders.permission.Permissions;
21 | import com.atlassian.bamboo.specs.api.builders.permission.PlanPermissions;
22 | import com.atlassian.bamboo.specs.api.builders.plan.PlanIdentifier;
23 | import com.atlassian.bamboo.specs.util.BambooServer;
24 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
25 |
26 | import javax.validation.constraints.NotEmpty;
27 | import javax.validation.constraints.NotNull;
28 | import java.util.ArrayList;
29 |
30 | public class ProjectPermissionModel {
31 | @NotEmpty
32 | @NotNull
33 | public String[] plans;
34 |
35 | @NotEmpty
36 | @NotNull
37 | public ArrayList permissions;
38 |
39 | public void publishPermissions(BambooServer bambooServer, UserPasswordCredentials adminUser) {
40 | for (String planKey : this.plans) {
41 | String[] parts = planKey.split("-");
42 | PlanIdentifier id = new PlanIdentifier(parts[0], parts[1]);
43 |
44 | Permissions permissions = new Permissions();
45 | this.permissions.forEach(x -> x.addToPermissions(permissions));
46 |
47 | // Ensure our admin user always has admin permission
48 | permissions.userPermissions(adminUser.getUsername(), PermissionType.ADMIN);
49 |
50 | permissions.loggedInUserPermissions(PermissionType.VIEW).anonymousUserPermissionView();
51 |
52 | bambooServer.publish(new PlanPermissions(id).permissions(permissions));
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/deployment/environment/EnvironmentPermissionModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.deployment.environment;
18 |
19 | import au.com.reece.de.bamboospecs.models.PermissionModel;
20 | import com.atlassian.bamboo.specs.api.builders.permission.EnvironmentPermissions;
21 | import com.atlassian.bamboo.specs.api.builders.permission.PermissionType;
22 | import com.atlassian.bamboo.specs.api.builders.permission.Permissions;
23 | import com.atlassian.bamboo.specs.util.BambooServer;
24 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
25 |
26 | import javax.validation.constraints.NotEmpty;
27 | import javax.validation.constraints.NotNull;
28 | import java.util.ArrayList;
29 |
30 | public class EnvironmentPermissionModel {
31 | @NotEmpty
32 | @NotNull
33 | public String[] names;
34 |
35 | @NotEmpty
36 | @NotNull
37 | public ArrayList permissions;
38 |
39 | public void publishPermissions(BambooServer bambooServer, UserPasswordCredentials adminUser, String deploymentName) {
40 | for (String name : this.names) {
41 |
42 | Permissions permissions = new Permissions();
43 |
44 | for (PermissionModel perm : this.permissions) {
45 | perm.addToPermissions(permissions);
46 | }
47 |
48 | // Ensure our admin user always has admin permission
49 | permissions.userPermissions(adminUser.getUsername(), PermissionType.VIEW, PermissionType.EDIT);
50 |
51 | permissions.loggedInUserPermissions(PermissionType.VIEW).anonymousUserPermissionView();
52 |
53 | bambooServer.publish(new EnvironmentPermissions(deploymentName)
54 | .environmentName(name)
55 | .permissions(permissions));
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/java/au/com/reece/de/bamboospecs/DeploymentIncludedTasksTest.java:
--------------------------------------------------------------------------------
1 | package au.com.reece.de.bamboospecs;
2 |
3 | import com.atlassian.bamboo.specs.util.SimpleUserPasswordCredentials;
4 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
5 | import org.junit.Test;
6 |
7 | import java.io.File;
8 |
9 | public class DeploymentIncludedTasksTest {
10 |
11 | private static final boolean PUBLISH = false;
12 |
13 | @Test
14 | public void verifyThatIncludedTaskProcessed() {
15 | DeploymentControl control = new DeploymentControl();
16 | UserPasswordCredentials users = new SimpleUserPasswordCredentials("xx", "xx");
17 | File file = new File(getClass().getResource("/deployment/branch-service/deployment-included-tasks.yaml").getPath());
18 | control.run(users, file, PUBLISH);
19 | }
20 |
21 | @Test
22 | public void verifyTasksProcessed() {
23 | DeploymentControl control = new DeploymentControl();
24 | UserPasswordCredentials users = new SimpleUserPasswordCredentials("xx", "xx");
25 | File file = new File(getClass().getResource("/deployment/branch-service/deployment-inline-tasks.yaml").getPath());
26 | control.run(users, file, PUBLISH);
27 | }
28 |
29 | @Test
30 | public void verifyBothTasksAndIncludedTasksProcessed() {
31 | DeploymentControl control = new DeploymentControl();
32 | UserPasswordCredentials users = new SimpleUserPasswordCredentials("xx", "xx");
33 | File file = new File(getClass().getResource("/deployment/branch-service/deployment-combined.yaml").getPath());
34 | control.run(users, file, PUBLISH);
35 | }
36 |
37 | @Test(expected = RuntimeException.class)
38 | public void shouldFailWhenRequiredPropertyMissingOnIncludedTasks() {
39 | DeploymentControl control = new DeploymentControl();
40 | UserPasswordCredentials users = new SimpleUserPasswordCredentials("xx", "xx");
41 | File file = new File(getClass().getResource("/deployment/branch-service/deployment-included-tasks-missing-property.yaml").getPath());
42 | control.run(users, file, PUBLISH);
43 | }
44 |
45 | @Test
46 | public void verifyIncludedTasksWithIncludedEnvironment() {
47 | DeploymentControl control = new DeploymentControl();
48 | UserPasswordCredentials users = new SimpleUserPasswordCredentials("xx", "xx");
49 | File file = new File(getClass().getResource("/deployment/branch-service/deployment.yaml").getPath());
50 | control.run(users, file, PUBLISH);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/deployment/DeploymentPermissionModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.deployment;
18 |
19 | import au.com.reece.de.bamboospecs.models.deployment.environment.EnvironmentPermissionModel;
20 | import au.com.reece.de.bamboospecs.models.PermissionModel;
21 | import com.atlassian.bamboo.specs.api.builders.permission.DeploymentPermissions;
22 | import com.atlassian.bamboo.specs.api.builders.permission.PermissionType;
23 | import com.atlassian.bamboo.specs.api.builders.permission.Permissions;
24 | import com.atlassian.bamboo.specs.util.BambooServer;
25 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
26 |
27 | import javax.validation.constraints.NotEmpty;
28 | import javax.validation.constraints.NotNull;
29 | import java.util.List;
30 |
31 | public class DeploymentPermissionModel {
32 | @NotEmpty
33 | @NotNull
34 | public String name;
35 |
36 | @NotEmpty
37 | @NotNull
38 | public List permissions;
39 |
40 | @NotEmpty
41 | @NotNull
42 | public List environments;
43 |
44 | public void publishPermissions(BambooServer bambooServer, UserPasswordCredentials adminUser) {
45 | Permissions permissions = new Permissions();
46 |
47 | for (PermissionModel perm: this.permissions) {
48 | perm.addToPermissions(permissions);
49 | }
50 |
51 | // Ensure our admin user always has admin permission
52 | permissions.userPermissions(adminUser.getUsername(), PermissionType.VIEW, PermissionType.EDIT);
53 |
54 | permissions.loggedInUserPermissions(PermissionType.VIEW).anonymousUserPermissionView();
55 |
56 | bambooServer.publish(new DeploymentPermissions(this.name).permissions(permissions));
57 |
58 | // now publish all the environment permissions
59 | this.environments.forEach(x -> x.publishPermissions(bambooServer, adminUser, this.name));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/test/resources/deployment/include/deployment-tasks.yaml:
--------------------------------------------------------------------------------
1 | - type: CLEAN
2 | description: Clean working directory
3 | - type: ARTEFACT
4 | description: Download Helm chart
5 | - type: VCS
6 | description: Git Checkout
7 | repositories:
8 | - name: oyster-scripts
9 | path: oyster-scripts
10 | - type: SCRIPT
11 | description: Create AWS Resources
12 | body: |
13 | echo "GIT Branch: ${bamboo.planRepository.1.branch}"
14 |
15 | if [ "${bamboo_aws_resources}" == true ] || [ "${bamboo_planRepository_1_branch}" == "feature/aws-messaging" ]; then
16 | echo "Unzip cloudformation templates"
17 | unzip cloudformation.zip
18 |
19 | echo "INFO: Getting credentials for '${bamboo_aws_account}' account"
20 | source aws-get-creds ${bamboo_aws_account}
21 |
22 | echo "INFO: Ensure '${bamboo_application_name}-${bamboo_stack_namespace}' is created and up-to-date"
23 | docker pull -q artifactory.reecenet.org:6555/utilities/hermitcrab
24 | docker run --rm --user "$(id -u):$(id -g)" --env-file "${AWSACCESSTEMP}" \
25 | --volume "$(pwd)/cloudformation/${bamboo_application_name}.yaml:/cfn/stack.template" \
26 | artifactory.reecenet.org:6555/utilities/hermitcrab \
27 | ${bamboo_application_name}-${bamboo_stack_namespace} up \
28 | --template /cfn/stack.template \
29 | --tags-input "{Name: ${bamboo_application_name}-${bamboo_stack_namespace}, CostCentre: ${bamboo_stack_cost_centre}, Environment: ${bamboo_stack_environment}, Owner: ${bamboo_stack_owner}}" \
30 | --override Namespace=${bamboo_stack_namespace} \
31 | --override AppSupportEnvironment=${bamboo_app_support_environment}
32 | fi
33 | - type: SCRIPT
34 | description: Rotate AWS API Keys
35 | body: |
36 | if [ "${bamboo_aws_resources}" == true ] || [ "${bamboo_planRepository_1_branch}" == "feature/aws-messaging" ]; then
37 | echo "Rotate AWS API Keys"
38 | set -u
39 | oyster-scripts/tools/aws-api-key-rotation.sh \
40 | -a "core-nonprod" \
41 | -u "${bamboo_application_name}-${bamboo_stack_namespace}" \
42 | -c "${bamboo_k8s_cluster}" \
43 | -n "${bamboo_stack_namespace}"
44 | fi
45 | - type: SCRIPT
46 | description: Rolling Deployment
47 | body: |
48 | set -eux
49 | chart_version=$(zgrep -m 1 -a 'version:' ./*.tgz | awk '{print $2}')
50 | oyster-scripts/oyster.sh deploy \
51 | -c "${bamboo_k8s_cluster}" \
52 | -n "${bamboo_deploy_environment}" \
53 | -a "${bamboo_application_name}" \
54 | -v "${chart_version}" \
55 | -t 1200
56 |
--------------------------------------------------------------------------------
/src/test/resources/deployment/include/deployment-environments.yaml:
--------------------------------------------------------------------------------
1 | environments:
2 | - environment: trstst03
3 | description: Inventory Test AU
4 | requirements:
5 | - name: pegasus
6 | notifications:
7 | - when: DEPLOYMENT_FAILED
8 | slack: https://hooks.slack.com/services/
9 | variables:
10 | k8s_cluster: non-production-internal-cluster
11 | aws_account: core-nonprod
12 | app_support_environment: nonprod
13 | stack_environment: test
14 | stack_namespace: trstst03
15 | stack_cost_centre: 3963
16 | stack_owner: Inventory
17 | stack_app_support_email: inventar.testing@reece.com.au
18 | # Path is relative to the root yaml file
19 | includedTasks: ../include/deployment-tasks.yaml
20 | triggers:
21 | - type: AFTER_SUCCESSFUL_BUILD_PLAN
22 | description: Deploy development branch
23 |
24 | - environment: trstst04
25 | description: Inventory Test NZ
26 | requirements:
27 | - name: pegasus
28 | notifications:
29 | - when: DEPLOYMENT_FAILED
30 | slack: https://hooks.slack.com/services/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
31 | variables:
32 | k8s_cluster: non-production-internal-cluster
33 | tasks:
34 | - type: CLEAN
35 | description: Clean working directory
36 | - type: ARTEFACT
37 | description: Download Helm chart
38 | - type: VCS
39 | description: Git Checkout
40 | repositories:
41 | - name: oyster-scripts
42 | path: oyster-scripts
43 | - type: SCRIPT
44 | description: Rolling Deployment
45 | body: |
46 | set -eux
47 | chart_version=$(zgrep -m 1 -a 'version:' ./*.tgz | awk '{print $2}')
48 | oyster-scripts/oyster.sh deploy \
49 | -c "${bamboo_k8s_cluster}" \
50 | -n "${bamboo_deploy_environment}" \
51 | -a "${bamboo_application_name}" \
52 | -v "${chart_version}" \
53 | -t 1200
54 | triggers:
55 | - type: AFTER_SUCCESSFUL_BUILD_PLAN
56 | description: Deploy development branch
57 |
58 | - environment: trstst05
59 | description: Inventory UAT NZ
60 | requirements:
61 | - name: pegasus
62 | notifications:
63 | - when: DEPLOYMENT_FAILED
64 | slack: https://hooks.slack.com/services/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
65 | variables:
66 | k8s_cluster: non-production-internal-cluster
67 | tasks:
68 | - type: CLEAN
69 | description: Clean working directory
70 | # Path is relative to the root yaml file
71 | includedTasks: ../include/deployment-tasks.yaml
72 | triggers:
73 | - type: AFTER_SUCCESSFUL_BUILD_PLAN
74 | description: Deploy development branch
75 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/PlanBranchManagementModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import au.com.reece.de.bamboospecs.models.enums.PlanBranchCreateStrategy;
20 | import com.atlassian.bamboo.specs.api.builders.plan.branches.BranchCleanup;
21 | import com.atlassian.bamboo.specs.api.builders.plan.branches.PlanBranchManagement;
22 |
23 | public class PlanBranchManagementModel {
24 |
25 | public PlanBranchCreateStrategy createStrategy = PlanBranchCreateStrategy.MANUALLY;
26 |
27 | public String branchPattern;
28 |
29 | public boolean issueLinkingEnabled = true;
30 |
31 | public Integer delayCleanAfterDelete;
32 | public Integer delayCleanAfterInactivity;
33 |
34 | public PlanBranchManagement asPlanBranchManagement() {
35 | // plan branch management - cleanup
36 | if (this.delayCleanAfterDelete == null) {
37 | this.delayCleanAfterDelete = 0;
38 | }
39 | BranchCleanup removedBranchCleanup = new BranchCleanup()
40 | .whenRemovedFromRepositoryAfterDays(this.delayCleanAfterDelete);
41 | if (this.delayCleanAfterInactivity != null) {
42 | removedBranchCleanup.whenInactiveInRepositoryAfterDays(this.delayCleanAfterInactivity);
43 | }
44 |
45 | PlanBranchManagement pbm = new PlanBranchManagement()
46 | .issueLinkingEnabled(this.issueLinkingEnabled)
47 | .delete(removedBranchCleanup);
48 |
49 | switch (this.createStrategy) {
50 | case MANUALLY:
51 | pbm.createManually();
52 | break;
53 | case ON_PULL_REQUEST:
54 | pbm.createForPullRequest();
55 | break;
56 | case ON_NEW_BRANCH:
57 | pbm.createForVcsBranch();
58 | break;
59 | case ON_BRANCH_PATTERN:
60 | if (this.branchPattern == null) {
61 | throw new RuntimeException("branchPattern is required for ON_BRANCH_PATTERN");
62 | }
63 | pbm.createForVcsBranchMatching(this.branchPattern);
64 | break;
65 | }
66 |
67 | pbm.triggerBuildsLikeParentPlan()
68 | .notificationLikeParentPlan();
69 |
70 | return pbm;
71 | }}
72 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/deployment/environment/EnvironmentsModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.deployment.environment;
18 |
19 | import com.fasterxml.jackson.databind.ObjectMapper;
20 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 |
24 | import javax.validation.ConstraintViolation;
25 | import javax.validation.Validation;
26 | import javax.validation.Validator;
27 | import javax.validation.constraints.NotNull;
28 | import java.io.File;
29 | import java.io.IOException;
30 | import java.util.Set;
31 |
32 | public class EnvironmentsModel {
33 | private static final Logger LOGGER = LoggerFactory.getLogger(EnvironmentsModel.class);
34 |
35 | @NotNull
36 | public EnvironmentModel[] environments;
37 |
38 | private String filename;
39 |
40 | public EnvironmentModel get(String name) {
41 | for (EnvironmentModel e : this.environments) {
42 | if (e.environment.equals(name)) {
43 | return e;
44 | }
45 | }
46 | throw new RuntimeException(
47 | String.format("Missing environment '%s' in included yaml '%s'", name, this.filename));
48 | }
49 |
50 | public static EnvironmentsModel readYAML(String filename) {
51 | LOGGER.info("Parsing environments YAML {}", filename);
52 |
53 | File yaml = new File(filename);
54 |
55 | ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
56 | Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
57 |
58 | EnvironmentsModel included;
59 | try {
60 | included = mapper.readValue(yaml, EnvironmentsModel.class);
61 | Set> violations = validator.validate(included);
62 | if (!violations.isEmpty()) {
63 | violations.forEach(x -> LOGGER.error("{}: {}", x.getPropertyPath(), x.getMessage()));
64 | throw new RuntimeException("Error parsing included environments from " + filename);
65 | }
66 | } catch (IOException e) {
67 | throw new RuntimeException("Error parsing included environments from " + filename, e);
68 | }
69 | included.filename = filename;
70 |
71 | return included;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/DeploymentControl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs;
18 |
19 | import au.com.reece.de.bamboospecs.models.deployment.DeploymentModel;
20 | import com.atlassian.bamboo.specs.util.BambooServer;
21 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
22 | import com.fasterxml.jackson.databind.ObjectMapper;
23 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
24 | import org.slf4j.Logger;
25 | import org.slf4j.LoggerFactory;
26 |
27 | import javax.validation.ConstraintViolation;
28 | import javax.validation.Validation;
29 | import javax.validation.Validator;
30 | import java.io.File;
31 | import java.io.IOException;
32 | import java.util.Set;
33 |
34 | public class DeploymentControl extends BambooController {
35 | private static final Logger LOGGER = LoggerFactory.getLogger(DeploymentControl.class);
36 |
37 | public void run(UserPasswordCredentials adminUser, String filePath, boolean publish) {
38 | run(adminUser, new File(filePath), publish);
39 | }
40 |
41 | public void run(UserPasswordCredentials adminUser, File yamlFile, boolean publish) {
42 | ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
43 | Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
44 |
45 | DeploymentModel yamlDeployment;
46 | try {
47 | yamlDeployment = mapper.readValue(yamlFile, DeploymentModel.class);
48 | Set> violations = validator.validate(yamlDeployment);
49 | if (!violations.isEmpty()) {
50 | violations.forEach(x -> LOGGER.error("{}: {}", x.getPropertyPath(), x.getMessage()));
51 | return;
52 | }
53 | } catch (Exception e) {
54 | throw new RuntimeException("Error reading YAML file: " + e.getMessage(), e);
55 | }
56 |
57 | // set the file path to the yaml file for includes
58 | yamlDeployment.yamlPath = yamlFile.getParentFile().getAbsolutePath();
59 |
60 | if (publish) {
61 | BambooServer bambooServer = new BambooServer(yamlDeployment.bambooServer, adminUser);
62 | yamlDeployment.publish(bambooServer, adminUser);
63 | } else {
64 | yamlDeployment.asDeployment();
65 | LOGGER.info("YAML parsed OK");
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/PermissionsControl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // Note: SSL fix needed, install CA certs for bamboo.reecenet.org using these instructions:
18 | // https://confluence.atlassian.com/kb/connecting-to-ssl-services-802171215.html
19 |
20 | package au.com.reece.de.bamboospecs;
21 |
22 | import au.com.reece.de.bamboospecs.models.PermissionFileModel;
23 | import com.atlassian.bamboo.specs.util.BambooServer;
24 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
25 | import com.fasterxml.jackson.databind.ObjectMapper;
26 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
27 | import org.slf4j.Logger;
28 | import org.slf4j.LoggerFactory;
29 |
30 | import javax.validation.ConstraintViolation;
31 | import javax.validation.Validation;
32 | import javax.validation.Validator;
33 | import java.io.File;
34 | import java.io.IOException;
35 | import java.util.Set;
36 |
37 | public class PermissionsControl extends BambooController {
38 | private static final Logger LOGGER = LoggerFactory.getLogger(PermissionsControl.class);
39 |
40 | public void run(UserPasswordCredentials adminUser, String filePath, boolean publish) {
41 | run(adminUser, new File(filePath), publish);
42 | }
43 |
44 | public void run(UserPasswordCredentials adminUser, File yamlFile, boolean publish) {
45 | ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
46 | Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
47 |
48 | PermissionFileModel yamlPermissions;
49 | try {
50 | yamlPermissions = mapper.readValue(yamlFile, PermissionFileModel.class);
51 | Set> violations = validator.validate(yamlPermissions);
52 | if (!violations.isEmpty()) {
53 | violations.forEach(x -> LOGGER.error("{}: {}", x.getPropertyPath(), x.getMessage()));
54 | return;
55 | }
56 | } catch (Exception e) {
57 | throw new RuntimeException("Error reading YAML file: " + e.getMessage(), e);
58 | }
59 |
60 | BambooServer bambooServer = new BambooServer(yamlPermissions.bambooServer, adminUser);
61 |
62 | if (publish) {
63 | yamlPermissions.publish(bambooServer, adminUser);
64 | } else {
65 | LOGGER.info("YAML parsed OK");
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/resources/deployment/branch-service/deployment-inline-tasks.yaml:
--------------------------------------------------------------------------------
1 | specType: deployment
2 | bambooServer: https://bamboo.reecenet.org/bamboo
3 | name: Cloudformation Deployment - Branch Service
4 | description: Deployment Plan for Branch Service Cloudformation Templates
5 | buildProject: BRANCHSHRDSRV
6 | buildPlan: BRANCHCFBUILD
7 | releaseNaming:
8 | pattern: 1.0.${bamboo.buildNumber}
9 | environments:
10 | - environment: trsdev02
11 | description: Product & Pricing Dev AU
12 | requirements:
13 | - name: pegasus
14 | notifications:
15 | - when: DEPLOYMENT_FAILED
16 | slack: https://hooks.slack.com/services/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
17 | variables:
18 | application_name: branch-service
19 | k8s_cluster: non-production-internal-cluster
20 | aws_account: core-nonprod
21 | app_support_environment: nonprod
22 | stack_environment: test
23 | stack_namespace: trsdev02
24 | stack_cost_centre: 3963
25 | stack_owner: Inventory
26 | triggers:
27 | tasks:
28 | - type: CLEAN
29 | description: Clean working directory
30 | - type: ARTEFACT
31 | description: Download Cloudformation Templates
32 | - type: VCS
33 | description: Git Checkout
34 | repositories:
35 | - name: oyster-scripts
36 | path: oyster-scripts
37 | - type: SCRIPT
38 | description: Install cloudformation templates to AWS
39 | body: |
40 | echo "Unzip cloudformation templates"
41 | unzip cloudformation.zip
42 |
43 | echo "INFO: Getting credentials for '${bamboo.aws_account}' account"
44 | source aws-get-creds ${bamboo_aws_account}
45 |
46 | echo "INFO: Ensure '${bamboo_application_name}-${bamboo_stack_namespace}' is created and up-to-date"
47 | docker pull -q artifactory.reecenet.org:6555/utilities/hermitcrab
48 | docker run --rm --user "$(id -u):$(id -g)" --env-file "${AWSACCESSTEMP}" \
49 | --volume "$(pwd)/cloudformation/${bamboo_application_name}.yaml:/cfn/stack.template" \
50 | artifactory.reecenet.org:6555/utilities/hermitcrab \
51 | ${bamboo_application_name}-${bamboo_stack_namespace} up \
52 | --template /cfn/stack.template \
53 | --tags-input "{Name: ${bamboo_application_name}-${bamboo_stack_namespace}, CostCentre: ${bamboo_stack_cost_centre}, Environment: ${bamboo_stack_environment}, Owner: ${bamboo_stack_owner}}" \
54 | --override Namespace=${bamboo_stack_namespace} \
55 | --override AppSupportEnvironment=${bamboo_app_support_environment}
56 | - type: SCRIPT
57 | description: Rotate AWS API Keys
58 | body: |
59 | echo "Rotate AWS API Keys"
60 | set -u
61 | oyster-scripts/tools/aws-api-key-rotation.sh \
62 | -a "${bamboo_aws_account}" \
63 | -u "${bamboo_application_name}-${bamboo_stack_namespace}" \
64 | -c "${bamboo_k8s_cluster}" \
65 | -n "${bamboo_stack_namespace}"
66 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/support/JUnitResultHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.support;
18 |
19 | import org.jetbrains.annotations.NotNull;
20 | import org.jtwig.JtwigModel;
21 | import org.jtwig.JtwigTemplate;
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 |
25 | import java.io.File;
26 | import java.io.FileOutputStream;
27 | import java.time.LocalTime;
28 | import java.util.HashMap;
29 | import java.util.Map;
30 |
31 | public class JUnitResultHelper {
32 | private static final Logger LOGGER = LoggerFactory.getLogger(JUnitResultHelper.class);
33 |
34 | public void handleOutcome(Exception exception, long time, String path) {
35 | String specResultName = path
36 | .replaceAll(System.getProperty("user.dir"), "")
37 | .replaceAll("/", "_")
38 | .replaceAll("-", "_");
39 |
40 | Map values = populateTemplateInformation(exception, time, specResultName);
41 |
42 | JtwigTemplate template = JtwigTemplate.classpathTemplate("resultTemplate.twig");
43 | JtwigModel model = JtwigModel.newModel(values);
44 |
45 | try {
46 | File resultDirectory = new File("results");
47 |
48 | if (!resultDirectory.exists() && !resultDirectory.mkdir()) {
49 | throw new RuntimeException("Failed to create directory at path " + resultDirectory.getAbsolutePath());
50 | }
51 |
52 | File destinationFile = new File("results/" + specResultName + ".xml");
53 |
54 | if (destinationFile.exists()) {
55 | String resultFileName = "results/" + specResultName + LocalTime.now().toString() + ".xml";
56 | LOGGER.warn("Destination XML file already exists - creating as {}", resultFileName);
57 | destinationFile = new File(resultFileName);
58 | }
59 |
60 | if (!destinationFile.createNewFile()) {
61 | throw new RuntimeException("Failed to create file " + destinationFile.getAbsolutePath());
62 | }
63 | template.render(model, new FileOutputStream(destinationFile));
64 | LOGGER.debug("Wrote JUnit XML to {}", destinationFile.getAbsolutePath());
65 | } catch (Exception ex) {
66 | throw new RuntimeException(ex);
67 | }
68 | }
69 |
70 | @NotNull
71 | private static Map populateTemplateInformation(Exception exception, long time, String specResultName) {
72 | Map values = new HashMap<>();
73 |
74 | values.put("time", time / 1000.0);
75 | values.put("specName", specResultName);
76 |
77 | if (exception == null) {
78 | values.put("successful", true);
79 | } else {
80 | values.put("successful", false);
81 | values.put("failureMessage", exception.getMessage());
82 | values.put("stacktrace", exception.getStackTrace());
83 | }
84 | return values;
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/TriggerModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import au.com.reece.de.bamboospecs.models.enums.TriggerType;
20 | import com.atlassian.bamboo.specs.api.builders.trigger.Trigger;
21 | import com.atlassian.bamboo.specs.builders.trigger.AfterSuccessfulBuildPlanTrigger;
22 | import com.atlassian.bamboo.specs.builders.trigger.BitbucketServerTrigger;
23 | import com.atlassian.bamboo.specs.builders.trigger.ScheduledTrigger;
24 |
25 | import javax.validation.constraints.NotEmpty;
26 | import javax.validation.constraints.NotNull;
27 | import java.time.DayOfWeek;
28 | import java.time.LocalTime;
29 | import java.time.format.DateTimeFormatter;
30 | import java.util.concurrent.TimeUnit;
31 |
32 | public class TriggerModel {
33 | @NotNull
34 | public TriggerType type;
35 |
36 | @NotNull
37 | @NotEmpty
38 | public String description;
39 |
40 | public String everyNumHours;
41 | public String dailyAt;
42 | public String weeklyAt;
43 | public String monthlyAt;
44 | public String cron;
45 | public String branch;
46 |
47 |
48 | static private LocalTime parseTimeNicely(String time) {
49 | return LocalTime.parse(time, DateTimeFormatter.ofPattern("H:mm"));
50 | }
51 |
52 | public Trigger asTrigger() {
53 | switch (this.type) {
54 | case AFTER_SUCCESSFUL_BUILD_PLAN:
55 | AfterSuccessfulBuildPlanTrigger buildTrigger = new AfterSuccessfulBuildPlanTrigger();
56 | buildTrigger.description(this.description);
57 | if (this.branch != null) {
58 | buildTrigger.triggerByBranch(this.branch);
59 | }
60 | return buildTrigger;
61 | case AFTER_STASH_COMMIT:
62 | return new BitbucketServerTrigger().description(this.description);
63 | case SCHEDULED:
64 | ScheduledTrigger scheduledTrigger = new ScheduledTrigger().description(this.description);
65 | if (this.everyNumHours != null) {
66 | scheduledTrigger.scheduleEvery(Integer.parseInt(this.everyNumHours), TimeUnit.HOURS);
67 | }
68 | if (this.dailyAt != null) {
69 | scheduledTrigger.scheduleOnceDaily(parseTimeNicely(dailyAt));
70 | }
71 | if (this.weeklyAt != null) {
72 | String[] parts = this.weeklyAt.split(" ");
73 | String upperDay = parts[0].toUpperCase();
74 | scheduledTrigger.scheduleWeekly(parseTimeNicely(parts[1]), DayOfWeek.valueOf(upperDay));
75 | }
76 | if (this.monthlyAt != null) {
77 | String[] parts = this.monthlyAt.split(" ");
78 | scheduledTrigger.scheduleMonthly(parseTimeNicely(parts[1]), Integer.getInteger(parts[0]));
79 | }
80 | if (this.cron != null) {
81 | scheduledTrigger.cronExpression(this.cron);
82 | }
83 | return scheduledTrigger;
84 | default:
85 | throw new RuntimeException("Unexpected 'type' value from yaml " + this.type);
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/NotificationModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import au.com.reece.de.bamboospecs.models.enums.NotificationTrigger;
20 | import com.atlassian.bamboo.specs.api.builders.AtlassianModule;
21 | import com.atlassian.bamboo.specs.api.builders.notification.AnyNotificationRecipient;
22 | import com.atlassian.bamboo.specs.api.builders.notification.Notification;
23 | import com.atlassian.bamboo.specs.api.builders.notification.NotificationRecipient;
24 | import com.atlassian.bamboo.specs.api.builders.notification.NotificationType;
25 | import com.atlassian.bamboo.specs.builders.notification.*;
26 |
27 | import javax.validation.constraints.NotNull;
28 | import java.util.ArrayList;
29 |
30 | public class NotificationModel extends DomainModel {
31 | @NotNull
32 | public NotificationTrigger when;
33 |
34 | public String slack;
35 |
36 | public String[] recipientGroups;
37 |
38 | public String[] recipientUsers;
39 |
40 | public Boolean responsibleUser;
41 |
42 | public Notification asNotification() {
43 | Notification notification = new Notification();
44 | ArrayList recipients = new ArrayList<>();
45 | NotificationType type;
46 |
47 | switch (this.when) {
48 | case PLAN_COMPLETED:
49 | type = new PlanCompletedNotification();
50 | break;
51 | case PLAN_FAILED:
52 | type = new PlanFailedNotification();
53 | break;
54 | case STATUS_CHANGED:
55 | type = new PlanStatusChangedNotification();
56 | break;
57 | case DEPLOYMENT_FAILED:
58 | type = new DeploymentFailedNotification();
59 | break;
60 | case DEPLOYMENT_FINISHED:
61 | type = new DeploymentFinishedNotification();
62 | break;
63 | case DEPLOYMENT_STARTED_AND_FINISHED:
64 | type = new DeploymentStartedAndFinishedNotification();
65 | break;
66 | default:
67 | // shouldn't actually be possible, given we load via enum
68 | throw new RuntimeException("Unexpected 'when' value from yaml " + this.when);
69 | }
70 |
71 | notification.type(type);
72 |
73 | if (this.slack != null) {
74 | recipients.add(new AnyNotificationRecipient(
75 | new AtlassianModule("com.atlassian.bamboo.plugins.bamboo-slack:recipient.slack"))
76 | .recipientString(slack));
77 | }
78 |
79 | if (this.recipientGroups != null) {
80 | for (String name : this.recipientGroups)
81 | recipients.add(new GroupRecipient(name));
82 | }
83 |
84 | if (this.recipientUsers != null) {
85 | for (String name : this.recipientUsers)
86 | recipients.add(new UserRecipient(name));
87 | }
88 |
89 | if (this.responsibleUser != null && this.responsibleUser) {
90 | recipients.add(new ResponsibleRecipient());
91 | }
92 |
93 | if (recipients.size() == 0) {
94 | throw new RuntimeException("No recipients defined for " + this.when);
95 | }
96 |
97 | return notification.recipients(recipients.toArray(new NotificationRecipient[]{}));
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/RepositoryModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.applink.ApplicationLink;
20 | import com.atlassian.bamboo.specs.api.builders.plan.Plan;
21 | import com.atlassian.bamboo.specs.api.builders.repository.VcsChangeDetection;
22 | import com.atlassian.bamboo.specs.api.builders.repository.VcsRepositoryIdentifier;
23 | import com.atlassian.bamboo.specs.builders.repository.bitbucket.server.BitbucketServerRepository;
24 | import com.atlassian.bamboo.specs.builders.repository.git.GitRepository;
25 | import com.atlassian.bamboo.specs.builders.repository.viewer.BitbucketServerRepositoryViewer;
26 | import com.atlassian.bamboo.specs.builders.task.CheckoutItem;
27 | import com.google.common.base.Strings;
28 |
29 | import javax.validation.constraints.NotEmpty;
30 | import javax.validation.constraints.NotNull;
31 |
32 | public class RepositoryModel extends DomainModel {
33 | @NotNull
34 | @NotEmpty
35 | public String name;
36 |
37 | public String projectKey;
38 |
39 | public String repositorySlug;
40 |
41 | public String gitURL;
42 |
43 | public String path;
44 |
45 | public String branch;
46 |
47 | public String triggerPattern;
48 |
49 | public boolean shallowClone = true;
50 |
51 | public boolean submodules = false;
52 |
53 | public CheckoutItem asCheckoutItem() {
54 | CheckoutItem vcs = new CheckoutItem().repository(new VcsRepositoryIdentifier().name(this.name));
55 | if (this.path != null && !this.path.isEmpty()) vcs.path(this.path);
56 | return vcs;
57 | }
58 |
59 | Plan addToPlan(Plan plan) {
60 | if (this.projectKey != null && !this.projectKey.isEmpty()) {
61 | if (this.repositorySlug == null || this.repositorySlug.isEmpty()) {
62 | throw new RuntimeException("Invalid repository (projectKey AND repositorySlug)");
63 | }
64 | BitbucketServerRepository stash = new BitbucketServerRepository()
65 | .name(this.name)
66 | .server(new ApplicationLink().name("Stash"))
67 | .projectKey(this.projectKey)
68 | .repositorySlug(this.repositorySlug)
69 | // set some "default" options
70 | .repositoryViewer(new BitbucketServerRepositoryViewer())
71 | .shallowClonesEnabled(shallowClone)
72 | .submodulesEnabled(submodules)
73 | .remoteAgentCacheEnabled(false);
74 | if (this.branch != null && !this.branch.isEmpty()) {
75 | stash.branch(this.branch);
76 | }
77 |
78 | if (!Strings.isNullOrEmpty(triggerPattern)) {
79 | stash.changeDetection(new VcsChangeDetection()
80 | .filterFilePatternOption(VcsChangeDetection.FileFilteringOption.INCLUDE_ONLY)
81 | .filterFilePatternRegex(triggerPattern)
82 | );
83 | }
84 |
85 | return plan.planRepositories(stash);
86 | } else if (this.gitURL != null && !this.gitURL.isEmpty()) {
87 | GitRepository git = new GitRepository();
88 | if (this.name == null || this.name.isEmpty()) {
89 | throw new RuntimeException("Invalid repository (needs gitURL AND path)");
90 | }
91 | git.name(this.name).url(this.gitURL);
92 | if (this.branch != null) {
93 | git.branch(this.branch);
94 | }
95 | return plan.planRepositories(git);
96 | }
97 | throw new RuntimeException("Invalid repository (missing projectKey or gitURL)");
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/test/resources/deployment/branch-service/deployment-combined.yaml:
--------------------------------------------------------------------------------
1 | specType: deployment
2 | bambooServer: https://bamboo.reecenet.org/bamboo
3 | name: Cloudformation Deployment - Branch Service
4 | description: Deployment Plan for Branch Service Cloudformation Templates
5 | buildProject: BRANCHSHRDSRV
6 | buildPlan: BRANCHCFBUILD
7 | releaseNaming:
8 | pattern: 1.0.${bamboo.buildNumber}
9 | environments:
10 | - environment: trsdev02
11 | description: Product & Pricing Dev AU
12 | requirements:
13 | - name: pegasus
14 | notifications:
15 | - when: DEPLOYMENT_FAILED
16 | slack: https://hooks.slack.com/services/
17 | variables:
18 | application_name: branch-service
19 | k8s_cluster: non-production-internal-cluster
20 | aws_account: core-nonprod
21 | app_support_environment: nonprod
22 | stack_environment: test
23 | stack_namespace: trsdev02
24 | stack_cost_centre: 3963
25 | stack_owner: Inventory
26 | triggers:
27 | tasks:
28 | - type: CLEAN
29 | description: Clean working directory
30 | - type: ARTEFACT
31 | description: Download Cloudformation Templates
32 | - type: VCS
33 | description: Git Checkout
34 | repositories:
35 | - name: oyster-scripts
36 | path: oyster-scripts
37 | - type: SCRIPT
38 | description: Install cloudformation templates to AWS
39 | body: |
40 | echo "Unzip cloudformation templates"
41 | unzip cloudformation.zip
42 |
43 | echo "INFO: Getting credentials for '${bamboo.aws_account}' account"
44 | source aws-get-creds ${bamboo_aws_account}
45 |
46 | echo "INFO: Ensure '${bamboo_application_name}-${bamboo_stack_namespace}' is created and up-to-date"
47 | docker pull -q artifactory.reecenet.org:6555/utilities/hermitcrab
48 | docker run --rm --user "$(id -u):$(id -g)" --env-file "${AWSACCESSTEMP}" \
49 | --volume "$(pwd)/cloudformation/${bamboo_application_name}.yaml:/cfn/stack.template" \
50 | artifactory.reecenet.org:6555/utilities/hermitcrab \
51 | ${bamboo_application_name}-${bamboo_stack_namespace} up \
52 | --template /cfn/stack.template \
53 | --tags-input "{Name: ${bamboo_application_name}-${bamboo_stack_namespace}, CostCentre: ${bamboo_stack_cost_centre}, Environment: ${bamboo_stack_environment}, Owner: ${bamboo_stack_owner}}" \
54 | --override Namespace=${bamboo_stack_namespace} \
55 | --override AppSupportEnvironment=${bamboo_app_support_environment}
56 | - type: SCRIPT
57 | description: Rotate AWS API Keys
58 | body: |
59 | echo "Rotate AWS API Keys"
60 | set -u
61 | oyster-scripts/tools/aws-api-key-rotation.sh \
62 | -a "${bamboo_aws_account}" \
63 | -u "${bamboo_application_name}-${bamboo_stack_namespace}" \
64 | -c "${bamboo_k8s_cluster}" \
65 | -n "${bamboo_stack_namespace}"
66 |
67 | - environment: trstst02
68 | description: Product & Pricing Test AU
69 | requirements:
70 | - name: pegasus
71 | notifications:
72 | - when: DEPLOYMENT_FAILED
73 | slack: https://hooks.slack.com/services/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
74 | variables:
75 | application_name: branch-service
76 | k8s_cluster: non-production-internal-cluster
77 | aws_account: core-nonprod
78 | app_support_environment: nonprod
79 | stack_environment: test
80 | stack_namespace: trstst02
81 | stack_cost_centre: 3963
82 | stack_owner: Inventory
83 | triggers:
84 | includedTasks: ../include/cloudformation-deploy-tasks.yaml
85 |
86 | - environment: trstst04
87 | description: Product & Pricing Test AU
88 | requirements:
89 | - name: pegasus
90 | notifications:
91 | - when: DEPLOYMENT_FAILED
92 | slack: https://hooks.slack.com/services/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
93 | variables:
94 | application_name: branch-service
95 | k8s_cluster: non-production-internal-cluster
96 | aws_account: core-nonprod
97 | app_support_environment: nonprod
98 | stack_environment: test
99 | stack_namespace: trstst04
100 | stack_cost_centre: 3963
101 | stack_owner: Inventory
102 | triggers:
103 | includedTasks: ../include/cloudformation-deploy-tasks.yaml
104 | tasks:
105 | - type: CLEAN
106 | description: Clean working directory
107 |
--------------------------------------------------------------------------------
/src/test/java/au/com/reece/de/bamboospecs/support/JUnitResultHelperTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.support;
18 |
19 | import org.apache.commons.io.FileUtils;
20 | import org.jetbrains.annotations.NotNull;
21 | import org.junit.Before;
22 | import org.junit.Test;
23 | import org.xmlunit.builder.DiffBuilder;
24 | import org.xmlunit.diff.*;
25 |
26 | import java.io.File;
27 | import java.io.IOException;
28 | import java.nio.charset.Charset;
29 | import java.nio.file.Files;
30 |
31 | import static org.hamcrest.core.Is.is;
32 | import static org.junit.Assert.assertThat;
33 |
34 | public class JUnitResultHelperTest {
35 | private JUnitResultHelper testInstance;
36 |
37 | @Before
38 | public void setUp() throws Exception {
39 | File resultsDirectory = new File("results");
40 | FileUtils.deleteDirectory(resultsDirectory);
41 | testInstance = new JUnitResultHelper();
42 | }
43 |
44 | @Test
45 | public void handleOutcome_passedTest() throws IOException {
46 | testInstance.handleOutcome(null, 1000, "alastair/test/plan.yaml");
47 |
48 | String result = getResultFile();
49 |
50 | String expectedResult = "\n" +
51 | "\n" +
52 | " \n" +
53 | " \n" +
54 | " \n" +
55 | "\n";
56 |
57 | Diff xmlDiff = DiffBuilder
58 | .compare(expectedResult)
59 | .withTest(result)
60 | .checkForSimilar()
61 | .build();
62 |
63 | assertThat(xmlDiff.hasDifferences(), is(false));
64 | }
65 |
66 | @Test
67 | public void handleOutcomeFileExists() throws IOException {
68 | testInstance.handleOutcome(null, 1000, "alastair/test/plan.yaml");
69 |
70 | testInstance.handleOutcome(null, 1000, "alastair/test/plan.yaml");
71 |
72 | File destination = new File("results");
73 | assertThat(destination.exists(), is(true));
74 |
75 | assertThat(destination.listFiles().length, is(2));
76 | }
77 |
78 | @NotNull
79 | private String getResultFile() throws IOException {
80 | File resultFile = new File("results/alastair_test_plan.yaml.xml");
81 | assertThat(resultFile.exists(), is(true));
82 |
83 | return new String(Files.readAllBytes(resultFile.toPath()), Charset.defaultCharset());
84 | }
85 |
86 | @Test
87 | public void handleOutcome_failedTest() throws IOException {
88 | RuntimeException exception = new RuntimeException("Oopsie doodle!");
89 |
90 | testInstance.handleOutcome(exception, 1000, "alastair/test/plan.yaml");
91 |
92 | String result = getResultFile();
93 |
94 | String expectedResult = "\n" +
95 | "\n" +
96 | " \n" +
97 | " \n" +
98 | " \n" +
99 | " \n" +
101 | " \n" +
102 | " \n" +
103 | " \n" +
104 | " \n" +
105 | "\n";
106 |
107 | Diff xmlDiff = DiffBuilder
108 | .compare(expectedResult)
109 | .withTest(result)
110 | .withDifferenceEvaluator((comparison, outcome) -> {
111 | if (comparison.getType() == ComparisonType.TEXT_VALUE
112 | && comparison.getControlDetails().getTarget().getParentNode().getLocalName().equalsIgnoreCase("failure")) {
113 | return ComparisonResult.SIMILAR;
114 | } else {
115 | return outcome;
116 | }
117 | })
118 | .checkForSimilar()
119 | .build();
120 |
121 | assertThat(xmlDiff.toString(), xmlDiff.hasDifferences(), is(false));
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/BuildModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import au.com.reece.de.bamboospecs.validation.NoIllegalCharacters;
20 | import com.atlassian.bamboo.specs.api.builders.Variable;
21 | import com.atlassian.bamboo.specs.api.builders.notification.Notification;
22 | import com.atlassian.bamboo.specs.api.builders.plan.Plan;
23 | import com.atlassian.bamboo.specs.api.builders.plan.Stage;
24 | import com.atlassian.bamboo.specs.api.builders.plan.configuration.AllOtherPluginsConfiguration;
25 | import com.atlassian.bamboo.specs.api.builders.plan.configuration.ConcurrentBuilds;
26 | import com.atlassian.bamboo.specs.api.builders.project.Project;
27 | import com.atlassian.bamboo.specs.api.builders.trigger.Trigger;
28 |
29 | import javax.validation.Valid;
30 | import javax.validation.constraints.NotEmpty;
31 | import javax.validation.constraints.NotNull;
32 | import java.util.ArrayList;
33 | import java.util.List;
34 | import java.util.Map;
35 | import java.util.stream.Collectors;
36 |
37 | public class BuildModel extends BambooYamlFileModel {
38 | public String yamlPath;
39 |
40 | @NotNull
41 | @NotEmpty
42 | public String projectKey;
43 |
44 | @NotNull
45 | @NotEmpty
46 | public String projectName;
47 |
48 | @NotNull
49 | @NotEmpty
50 | public String planKey;
51 |
52 | @NotNull
53 | @NotEmpty
54 | public String planName;
55 |
56 | @NotNull
57 | @NotEmpty
58 | @NoIllegalCharacters
59 | public String description;
60 |
61 | public Integer maximumConcurrentBuilds;
62 |
63 | public List labels;
64 |
65 | // TODO deprecate repository
66 | public RepositoryModel repository;
67 | public List<@Valid RepositoryModel> repositories;
68 | public String[] linkedRepositories;
69 |
70 | public Map variables;
71 |
72 | // branch management has sensible defaults
73 | public PlanBranchManagementModel branchManagement = new PlanBranchManagementModel();
74 |
75 | @NotNull
76 | public List<@Valid NotificationModel> notifications;
77 |
78 | @NotNull
79 | public List<@Valid StageModel> stages;
80 |
81 | public DependencyModel dependencies;
82 |
83 | public List<@Valid TriggerModel> triggers;
84 |
85 | public Plan getPlan() {
86 | Project project = new Project().key(this.projectKey);
87 | project.name(projectName);
88 | Plan plan = new Plan(project, this.planName, this.planKey);
89 | plan.description(this.description);
90 |
91 | plan.notifications(this.notifications.stream().map(NotificationModel::asNotification)
92 | .collect(Collectors.toList()).toArray(new Notification[]{}));
93 |
94 | this.addPluginConfiguration(plan);
95 |
96 | if (this.repository != null) {
97 | this.repository.addToPlan(plan);
98 | }
99 |
100 | if (this.linkedRepositories != null) {
101 | plan.linkedRepositories(this.linkedRepositories);
102 | }
103 |
104 | if (this.repositories != null) {
105 | for (RepositoryModel repos : this.repositories) {
106 | repos.addToPlan(plan);
107 | }
108 | }
109 |
110 | ArrayList variables = new ArrayList<>();
111 | if (this.variables != null) {
112 | for (String key : this.variables.keySet()) {
113 | variables.add(new Variable(key, this.variables.get(key)));
114 | }
115 | plan.variables(variables.toArray(new Variable[0]));
116 | }
117 |
118 | this.stages.forEach(x -> x.yamlPath = this.yamlPath);
119 |
120 | plan.stages(this.stages.stream().map(StageModel::asStage)
121 | .collect(Collectors.toList()).toArray(new Stage[]{}));
122 |
123 | plan.planBranchManagement(this.branchManagement.asPlanBranchManagement());
124 |
125 | if (this.dependencies != null) this.dependencies.addToPlan(plan);
126 |
127 | if (this.triggers != null) {
128 | plan.triggers(this.triggers.stream().map(TriggerModel::asTrigger)
129 | .collect(Collectors.toList()).toArray(new Trigger[]{}));
130 | }
131 |
132 | return plan;
133 | }
134 |
135 | private void addPluginConfiguration(Plan plan) {
136 | // this is the basic configuration needed
137 | if (maximumConcurrentBuilds == null) {
138 | maximumConcurrentBuilds = 1;
139 | }
140 | plan.pluginConfigurations(
141 | new ConcurrentBuilds().useSystemWideDefault(false).maximumNumberOfConcurrentBuilds(maximumConcurrentBuilds),
142 | new AllOtherPluginsConfiguration()
143 | );
144 | }
145 |
146 | public Boolean hasLabels() {
147 | return this.labels != null && !this.labels.isEmpty();
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/deployment/DeploymentModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package au.com.reece.de.bamboospecs.models.deployment;
17 |
18 | import au.com.reece.de.bamboospecs.models.BambooYamlFileModel;
19 | import au.com.reece.de.bamboospecs.models.IncludeEnvironmentsModel;
20 | import au.com.reece.de.bamboospecs.models.ReleaseNamingModel;
21 | import au.com.reece.de.bamboospecs.models.deployment.environment.EnvironmentModel;
22 | import com.atlassian.bamboo.specs.api.builders.Variable;
23 | import com.atlassian.bamboo.specs.api.builders.deployment.Deployment;
24 | import com.atlassian.bamboo.specs.api.builders.deployment.Environment;
25 | import com.atlassian.bamboo.specs.api.builders.permission.DeploymentPermissions;
26 | import com.atlassian.bamboo.specs.api.builders.permission.EnvironmentPermissions;
27 | import com.atlassian.bamboo.specs.api.builders.permission.PermissionType;
28 | import com.atlassian.bamboo.specs.api.builders.permission.Permissions;
29 | import com.atlassian.bamboo.specs.api.builders.plan.PlanIdentifier;
30 | import com.atlassian.bamboo.specs.util.BambooServer;
31 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
32 |
33 | import javax.validation.constraints.NotEmpty;
34 | import javax.validation.constraints.NotNull;
35 | import java.util.ArrayList;
36 | import java.util.List;
37 | import java.util.Map;
38 |
39 | public class DeploymentModel extends BambooYamlFileModel {
40 |
41 | public String yamlPath;
42 |
43 | @NotNull
44 | @NotEmpty
45 | public String name;
46 |
47 | @NotNull
48 | @NotEmpty
49 | public String buildProject;
50 |
51 | @NotNull
52 | @NotEmpty
53 | public String buildPlan;
54 |
55 | @NotNull
56 | @NotEmpty
57 | public String description;
58 |
59 | @NotNull
60 | public ReleaseNamingModel releaseNaming;
61 |
62 | public Map variables;
63 |
64 | public List environments;
65 |
66 | public IncludeEnvironmentsModel includeEnvironments;
67 |
68 | private final ArrayList collectedEnvironments = new ArrayList<>();
69 |
70 | public DeploymentPermissionsModel permissions;
71 |
72 | public Deployment asDeployment() {
73 | Deployment deployment = new Deployment(new PlanIdentifier(this.buildProject, this.buildPlan), this.name)
74 | .description(this.description)
75 | .releaseNaming(this.releaseNaming.asReleaseNaming());
76 |
77 | // collect all the environments
78 | collectAllEnvironments();
79 |
80 | // convert to array of Bamboo Environment
81 | Environment[] environments = this.collectedEnvironments.stream()
82 | .map(EnvironmentModel::asEnvironment)
83 | .toArray(Environment[]::new);
84 |
85 | // attach our "global" variables
86 | attachVariablesToEnvironments(environments);
87 |
88 | return deployment.environments(environments);
89 | }
90 |
91 | private void collectAllEnvironments() {
92 | if (this.environments != null) {
93 | this.environments.forEach(environmentModel -> environmentModel.yamlPath = this.yamlPath);
94 | this.collectedEnvironments.addAll(this.environments);
95 | }
96 | if (this.includeEnvironments != null) {
97 | this.includeEnvironments.addEnvironments(this.collectedEnvironments, this.yamlPath);
98 | }
99 | }
100 |
101 | private void attachVariablesToEnvironments(Environment[] environments) {
102 | if (this.variables != null) {
103 | ArrayList variables = new ArrayList<>();
104 | for (String key : this.variables.keySet()) {
105 | variables.add(new Variable(key, this.variables.get(key)));
106 | }
107 | Variable[] var_array = variables.toArray(new Variable[0]);
108 | for (Environment environment : environments) {
109 | environment.variables(var_array);
110 | }
111 | }
112 | }
113 |
114 | public void publish(BambooServer bambooServer, UserPasswordCredentials adminUser) {
115 | Deployment deployment = this.asDeployment();
116 | bambooServer.publish(deployment);
117 |
118 | if (this.permissions != null) {
119 | Permissions permissions = new Permissions();
120 |
121 | // Ensure our admin user always has admin permission
122 | permissions.userPermissions(adminUser.getUsername(), PermissionType.VIEW, PermissionType.EDIT);
123 |
124 | permissions.loggedInUserPermissions(PermissionType.VIEW).anonymousUserPermissionView();
125 |
126 | // add the project permissions and publish those
127 | this.permissions.project.addToPermissions(permissions);
128 | bambooServer.publish(new DeploymentPermissions(this.name).permissions(permissions));
129 |
130 | // now the per-environment permissions
131 | permissions = new Permissions();
132 | this.permissions.environment.addToPermissions(permissions);
133 | for (EnvironmentModel e : this.collectedEnvironments) {
134 | bambooServer.publish(new EnvironmentPermissions(this.name).environmentName(e.environment).permissions(permissions));
135 | }
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/deployment/environment/EnvironmentModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models.deployment.environment;
18 |
19 | import au.com.reece.de.bamboospecs.models.NotificationModel;
20 | import au.com.reece.de.bamboospecs.models.RequirementModel;
21 | import au.com.reece.de.bamboospecs.models.TaskModel;
22 | import au.com.reece.de.bamboospecs.models.TriggerModel;
23 | import com.atlassian.bamboo.specs.api.builders.Variable;
24 | import com.atlassian.bamboo.specs.api.builders.deployment.Environment;
25 | import com.atlassian.bamboo.specs.api.builders.notification.Notification;
26 | import com.atlassian.bamboo.specs.api.builders.requirement.Requirement;
27 | import com.atlassian.bamboo.specs.api.builders.task.Task;
28 | import com.atlassian.bamboo.specs.api.builders.trigger.Trigger;
29 | import com.fasterxml.jackson.databind.ObjectMapper;
30 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
31 | import org.slf4j.Logger;
32 | import org.slf4j.LoggerFactory;
33 |
34 | import javax.validation.ConstraintViolation;
35 | import javax.validation.Valid;
36 | import javax.validation.Validation;
37 | import javax.validation.Validator;
38 | import javax.validation.constraints.NotEmpty;
39 | import javax.validation.constraints.NotNull;
40 | import java.io.File;
41 | import java.io.IOException;
42 | import java.nio.file.Path;
43 | import java.nio.file.Paths;
44 | import java.util.ArrayList;
45 | import java.util.Arrays;
46 | import java.util.List;
47 | import java.util.Map;
48 | import java.util.Set;
49 | import java.util.stream.Collectors;
50 |
51 | public class EnvironmentModel {
52 | private static final Logger LOGGER = LoggerFactory.getLogger(EnvironmentModel.class);
53 |
54 | public String yamlPath;
55 |
56 | @NotNull
57 | @NotEmpty
58 | public String environment;
59 |
60 | @NotNull
61 | @NotEmpty
62 | public String description;
63 |
64 | @NotNull
65 | @NotEmpty
66 | public List<@Valid TaskModel> tasks;
67 |
68 | public List<@Valid TriggerModel> triggers;
69 |
70 | public List<@Valid NotificationModel> notifications;
71 |
72 | public List<@Valid RequirementModel> requirements;
73 |
74 | public Map variables;
75 |
76 | public String includedTasks;
77 |
78 | public Environment asEnvironment() {
79 | List tasks = addTasks();
80 |
81 | Environment environment = new Environment(this.environment)
82 | .tasks(tasks.toArray(new Task[0]))
83 | .description(this.description);
84 |
85 | if (this.triggers != null) {
86 | Trigger[] triggers = this.triggers.stream().map(TriggerModel::asTrigger)
87 | .collect(Collectors.toList()).toArray(new Trigger[]{});
88 | environment.triggers(triggers);
89 | }
90 |
91 | if (this.notifications != null) {
92 | Notification notifications[] = this.notifications.stream().map(NotificationModel::asNotification)
93 | .collect(Collectors.toList()).toArray(new Notification[]{});
94 | environment.notifications(notifications);
95 | }
96 |
97 | if (this.requirements != null) {
98 | Requirement requirements[] = this.requirements.stream().map(RequirementModel::asRequirement)
99 | .collect(Collectors.toList()).toArray(new Requirement[]{});
100 | environment.requirements(requirements);
101 | }
102 |
103 | if (this.variables != null) {
104 | ArrayList variables = new ArrayList<>();
105 | for (String key : this.variables.keySet()) {
106 | variables.add(new Variable(key, this.variables.get(key)));
107 | }
108 | environment.variables(variables.toArray(new Variable[0]));
109 | }
110 | return environment;
111 | }
112 |
113 | private List addTasks() {
114 | List tasks = new ArrayList<>();
115 | if (this.tasks != null) {
116 | this.tasks.forEach(x -> x.yamlPath = yamlPath);
117 | tasks.addAll(this.tasks.stream().map(TaskModel::asTask).collect(Collectors.toList()));
118 | }
119 | if (this.includedTasks != null) {
120 | Path includedYaml = Paths.get(this.yamlPath, this.includedTasks);
121 | tasks.addAll(EnvironmentModel.tasksFromYAML(includedYaml.toString()));
122 | }
123 | return tasks;
124 | }
125 |
126 | private static List tasksFromYAML(String filename) {
127 | LOGGER.info("Parsing tasks YAML {}", filename);
128 |
129 | File yaml = new File(filename);
130 |
131 | ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
132 | Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
133 |
134 | TaskModel[] included;
135 | try {
136 | included = mapper.readValue(yaml, TaskModel[].class);
137 | for (TaskModel task : included) {
138 | Set> violations = validator.validate(task);
139 | if (!violations.isEmpty()) {
140 | violations.forEach(x -> LOGGER.error("{}: {}", x.getPropertyPath(), x.getMessage()));
141 | throw new RuntimeException("Error parsing included task from " + filename);
142 | }
143 | }
144 | } catch (IOException e) {
145 | throw new RuntimeException("Error parsing included tasks from " + filename, e);
146 | }
147 | return Arrays.stream(included).map(TaskModel::asTask).collect(Collectors.toList());
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/BuildControl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // Note: SSL fix needed, install CA certs for bamboo.reecenet.org using these instructions:
18 | // https://confluence.atlassian.com/kb/connecting-to-ssl-services-802171215.html
19 |
20 | package au.com.reece.de.bamboospecs;
21 |
22 | import au.com.reece.de.bamboospecs.models.BuildModel;
23 | import com.atlassian.bamboo.specs.api.builders.plan.Plan;
24 | import com.atlassian.bamboo.specs.api.exceptions.BambooSpecsPublishingException;
25 | import com.atlassian.bamboo.specs.util.BambooServer;
26 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
27 | import com.fasterxml.jackson.databind.ObjectMapper;
28 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
29 | import okhttp3.*;
30 | import org.slf4j.Logger;
31 | import org.slf4j.LoggerFactory;
32 |
33 | import javax.validation.ConstraintViolation;
34 | import javax.validation.Validation;
35 | import javax.validation.Validator;
36 | import java.io.File;
37 | import java.io.IOException;
38 | import java.util.ArrayList;
39 | import java.util.Set;
40 |
41 | public class BuildControl extends BambooController {
42 | private static final Logger LOGGER = LoggerFactory.getLogger(BuildControl.class);
43 | public static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
44 |
45 | public void run(UserPasswordCredentials adminUser, File yamlFile, boolean publish) {
46 | ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
47 | Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
48 |
49 | LOGGER.info("Parsing YAML {}", yamlFile.toPath());
50 |
51 | BuildModel yamlPlan;
52 | try {
53 | yamlPlan = mapper.readValue(yamlFile, BuildModel.class);
54 |
55 | Set> violations = validator.validate(yamlPlan);
56 | if (!violations.isEmpty()) {
57 | StringBuilder allErrors = new StringBuilder();
58 |
59 | violations.forEach(violation -> {
60 | allErrors.append(violation.getPropertyPath()).append(": ").append(violation.getMessage());
61 | LOGGER.error("{}: {}", violation.getPropertyPath(), violation.getMessage());
62 | });
63 |
64 | throw new RuntimeException("The YAML file (" + yamlFile.getName() + ") failed validation. See message(s): " + allErrors);
65 | }
66 | } catch (Exception e) {
67 | throw new RuntimeException("Error reading YAML file: " + e.getMessage(), e);
68 | }
69 |
70 | // set the file path to the yaml file for includes
71 | yamlPlan.yamlPath = yamlFile.getParentFile().getAbsolutePath();
72 |
73 | BambooServer bambooServer = new BambooServer(yamlPlan.bambooServer, adminUser);
74 |
75 | Plan plan = yamlPlan.getPlan();
76 | if (publish) {
77 | publishPlanWithRetry(bambooServer, plan);
78 |
79 | try {
80 | publishLabels(yamlPlan, adminUser);
81 | } catch (IOException e) {
82 | e.printStackTrace();
83 | }
84 | } else {
85 | LOGGER.info("YAML parsed OK");
86 | }
87 | }
88 |
89 | private void publishPlanWithRetry(BambooServer bambooServer, Plan plan) {
90 | int tries = 0;
91 | do {
92 | try {
93 | tries += 1;
94 | bambooServer.publish(plan);
95 | break;
96 | } catch (BambooSpecsPublishingException e) {
97 | // Bamboo is stupid. It gets confused by HTTP 302
98 | if (!e.getMessage().contains("HTTP 302") || tries >= 4) {
99 | throw e;
100 | }
101 | try {
102 | Thread.sleep(5_000);
103 | } catch (InterruptedException ex) {
104 | ex.printStackTrace();
105 | }
106 | }
107 | } while (true);
108 | }
109 |
110 | private void publishLabels(BuildModel yamlPlan, UserPasswordCredentials adminUser) throws IOException {
111 | if (!yamlPlan.hasLabels()) {
112 | yamlPlan.labels = new ArrayList<>();
113 | }
114 |
115 | yamlPlan.labels.add("poweredBySpecs");
116 |
117 | OkHttpClient client = new OkHttpClient();
118 |
119 | String url = String.format("%s/rest/api/latest/plan/%s-%s/label.json", yamlPlan.bambooServer, yamlPlan.projectKey, yamlPlan.planKey);
120 |
121 | for (String label : yamlPlan.labels) {
122 | // Friends don't let friends hand-write JSON
123 | String json = String.format("{\"name\":\"%s\"}", label);
124 |
125 | RequestBody body = RequestBody.create(JSON_MEDIA_TYPE, json);
126 | String credentials = Credentials.basic(adminUser.getUsername(), adminUser.getPassword());
127 | Request request = new Request.Builder()
128 | .url(url)
129 | .post(body)
130 | .addHeader("Authorization", credentials)
131 | .build();
132 |
133 | Response response = client.newCall(request).execute();
134 | if (!response.isSuccessful()) {
135 | throw new RuntimeException("Failed to publish labels. The HTTP request was unsuccessful due to the following: " + response.message());
136 | } else {
137 | LOGGER.info("Added label {} to build {}-{}", label, yamlPlan.projectKey, yamlPlan.planKey);
138 | }
139 | }
140 | }
141 |
142 | public void run(UserPasswordCredentials adminUser, String filePath, boolean publish) {
143 | run(adminUser, new File(filePath), publish);
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/StageJobModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import com.atlassian.bamboo.specs.api.builders.docker.DockerConfiguration;
20 | import com.atlassian.bamboo.specs.api.builders.plan.Job;
21 | import com.atlassian.bamboo.specs.api.builders.plan.artifact.Artifact;
22 | import com.atlassian.bamboo.specs.api.builders.plan.artifact.ArtifactSubscription;
23 | import com.atlassian.bamboo.specs.api.builders.plan.configuration.AllOtherPluginsConfiguration;
24 | import com.atlassian.bamboo.specs.api.builders.requirement.Requirement;
25 | import com.atlassian.bamboo.specs.api.builders.task.Task;
26 | import com.fasterxml.jackson.databind.ObjectMapper;
27 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
28 | import org.slf4j.Logger;
29 | import org.slf4j.LoggerFactory;
30 |
31 | import javax.validation.ConstraintViolation;
32 | import javax.validation.Valid;
33 | import javax.validation.Validation;
34 | import javax.validation.Validator;
35 | import java.io.File;
36 | import java.io.IOException;
37 | import java.nio.file.Path;
38 | import java.nio.file.Paths;
39 | import java.util.List;
40 | import java.util.Set;
41 | import java.util.stream.Collectors;
42 |
43 | public class StageJobModel extends DomainModel {
44 | private static final Logger LOGGER = LoggerFactory.getLogger(StageJobModel.class);
45 |
46 | public String yamlPath;
47 |
48 | public String include;
49 |
50 | public String name;
51 |
52 | public String key;
53 |
54 | public String description;
55 |
56 | public List<@Valid RequirementModel> requirements;
57 |
58 | public List<@Valid ArtifactModel> artifacts;
59 |
60 | public List<@Valid ArtifactSubscriptionModel> artifactSubscriptions;
61 |
62 | public List<@Valid TaskModel> tasks;
63 |
64 | public List<@Valid TaskModel> finalTasks;
65 |
66 | public String dockerContainer;
67 |
68 | public Job asJob() {
69 | if (this.include != null) {
70 | Path includedYaml = Paths.get(this.yamlPath, this.include);
71 | StageJobModel included = StageJobModel.fromYAML(includedYaml.toString());
72 | return included.asJob();
73 | }
74 |
75 | if (this.name == null) throw new IllegalArgumentException("Stage jobs require a 'name'");
76 | if (this.key == null) throw new IllegalArgumentException("Stage jobs require a 'key'");
77 | if (this.description == null) throw new IllegalArgumentException("Stage jobs require a 'description'");
78 | if (this.requirements == null) throw new IllegalArgumentException("Stage jobs require 'requirements'");
79 | if (this.tasks == null) throw new IllegalArgumentException("Stage jobs require 'tasks'");
80 |
81 | Job job = new Job(this.name, this.key);
82 | job.description(this.description);
83 |
84 | // Currently we have no need to configure plugins, but we have to explicitly do this
85 | // here in case older plans have plugin configuration dregs we can't delete
86 | // through the UI (like clover configs in Diary Notes)
87 | job.pluginConfigurations(new AllOtherPluginsConfiguration());
88 |
89 | if (this.artifacts != null) {
90 | Artifact[] artifacts = this.artifacts.stream().map(ArtifactModel::asArtifact)
91 | .collect(Collectors.toList()).toArray(new Artifact[]{});
92 | job.artifacts(artifacts);
93 | }
94 |
95 | if (this.artifactSubscriptions != null) {
96 | ArtifactSubscription[] subscriptions = this.artifactSubscriptions.stream()
97 | .map(ArtifactSubscriptionModel::asArtifactSubscription)
98 | .collect(Collectors.toList()).toArray(new ArtifactSubscription[]{});
99 | job.artifactSubscriptions(subscriptions);
100 | }
101 |
102 | this.tasks.forEach(x -> x.yamlPath = yamlPath);
103 |
104 | Task[] tasks = this.tasks.stream().map(TaskModel::asTask)
105 | .collect(Collectors.toList()).toArray(new Task[]{});
106 | job.tasks(tasks);
107 |
108 | if (this.finalTasks != null) {
109 | tasks = this.finalTasks.stream().map(TaskModel::asTask)
110 | .collect(Collectors.toList()).toArray(new Task[]{});
111 | job.finalTasks(tasks);
112 | }
113 |
114 | Requirement[] requirements = this.requirements.stream().map(RequirementModel::asRequirement)
115 | .collect(Collectors.toList()).toArray(new Requirement[]{});
116 | job.requirements(requirements);
117 |
118 | if (this.dockerContainer != null && !"".equalsIgnoreCase(this.dockerContainer)) {
119 | job.dockerConfiguration(new DockerConfiguration().image(this.dockerContainer));
120 | }
121 |
122 | return job;
123 | }
124 |
125 | public static StageJobModel fromYAML(String filename) {
126 | LOGGER.info("Parsing job YAML {}", filename);
127 |
128 | File yaml = new File(filename);
129 |
130 | ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
131 | Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
132 |
133 | StageJobModel included;
134 | try {
135 | included = mapper.readValue(yaml, StageJobModel.class);
136 | Set> violations = validator.validate(included);
137 | if (!violations.isEmpty()) {
138 | violations.forEach(x -> LOGGER.error("{}: {}", x.getPropertyPath(), x.getMessage()));
139 | throw new RuntimeException("Error parsing included job from " + filename);
140 | }
141 | } catch (IOException e) {
142 | throw new RuntimeException("Error parsing included job from " + filename, e);
143 | }
144 |
145 | return included;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/ReeceSpecs.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs;
18 |
19 | import au.com.reece.de.bamboospecs.models.BambooYamlFileModel;
20 | import au.com.reece.de.bamboospecs.support.JUnitResultHelper;
21 | import com.atlassian.bamboo.specs.util.FileUserPasswordCredentials;
22 | import com.atlassian.bamboo.specs.util.SimpleUserPasswordCredentials;
23 | import com.atlassian.bamboo.specs.util.UserPasswordCredentials;
24 | import com.fasterxml.jackson.databind.DeserializationFeature;
25 | import com.fasterxml.jackson.databind.ObjectMapper;
26 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
27 | import org.apache.commons.cli.*;
28 | import org.apache.commons.lang3.time.StopWatch;
29 | import org.jetbrains.annotations.NotNull;
30 | import org.slf4j.Logger;
31 | import org.slf4j.LoggerFactory;
32 |
33 | import javax.validation.ConstraintViolation;
34 | import javax.validation.Validation;
35 | import javax.validation.Validator;
36 | import java.io.File;
37 | import java.util.Set;
38 |
39 | public class ReeceSpecs {
40 | private static final Logger LOGGER = LoggerFactory.getLogger(ReeceSpecs.class);
41 | private final JUnitResultHelper resultHelper;
42 |
43 | public static void main(final String[] args) throws Exception {
44 | ReeceSpecs specs = new ReeceSpecs();
45 | specs.runSpecs(args);
46 | }
47 |
48 | private ReeceSpecs() {
49 | this(new JUnitResultHelper());
50 | }
51 |
52 | ReeceSpecs(JUnitResultHelper resultHelper) {
53 | this.resultHelper = resultHelper;
54 | }
55 |
56 | private void runSpecs(String[] args) throws ParseException {
57 | Options options = getCommandLineOptions();
58 |
59 | CommandLineParser parser = new DefaultParser();
60 | CommandLine cmd = parser.parse(options, args);
61 |
62 | if (cmd.hasOption("h")) {
63 | printHelp(options);
64 | return;
65 | }
66 |
67 | UserPasswordCredentials adminUser = setupCredentials(cmd);
68 |
69 | boolean publish = determinePublishing(cmd);
70 |
71 | if (cmd.getArgList().isEmpty()) {
72 | printHelp(options);
73 | throw new RuntimeException("Error: missing required file(s)");
74 | }
75 |
76 | for (String path : cmd.getArgList()) {
77 | runFileProcess(adminUser, publish, path);
78 | }
79 | }
80 |
81 | void runFileProcess(UserPasswordCredentials adminUser, boolean publish, String path) {
82 | StopWatch stopWatch = new StopWatch();
83 | stopWatch.start();
84 | Exception exception = null;
85 | try {
86 | BambooYamlFileModel bambooFile;
87 | bambooFile = readAndValidateYamlFile(path);
88 | BambooController controller = BambooController.getBambooController(path, bambooFile);
89 | controller.run(adminUser, path, publish);
90 | } catch (Exception ex) {
91 | exception = ex;
92 | LOGGER.error("An exception occurred: {}", ex.getMessage());
93 | } finally {
94 | stopWatch.stop();
95 | }
96 | resultHelper.handleOutcome(exception, stopWatch.getTime(), path);
97 | }
98 |
99 | private static boolean determinePublishing(CommandLine cmd) {
100 | if (cmd.hasOption("t")) {
101 | LOGGER.info("Parsing yaml only, not publishing");
102 | return false;
103 | }
104 | return true;
105 | }
106 |
107 | @NotNull
108 | private static UserPasswordCredentials setupCredentials(CommandLine cmd) {
109 | UserPasswordCredentials adminUser;
110 | if (cmd.hasOption("u")) {
111 | String username = cmd.getOptionValue("u");
112 | String password;
113 | if (cmd.hasOption("p")) {
114 | password = cmd.getOptionValue("p");
115 | } else {
116 | password = new String(System.console().readPassword("Enter password for '%s': ", username));
117 | }
118 | adminUser = new SimpleUserPasswordCredentials(username, password);
119 | } else {
120 | adminUser = new FileUserPasswordCredentials(cmd.getOptionValue("c", "./.credentials"));
121 | }
122 | return adminUser;
123 | }
124 |
125 | @NotNull
126 | private static Options getCommandLineOptions() {
127 | Options options = new Options();
128 |
129 | options.addOption("t", false, "Parse yaml only, do not publish");
130 | options.addOption("u", true, "Bamboo user to publish as");
131 | options.addOption("p", true, "Bamboo user's password");
132 | options.addOption("c", true, "Credentials file with Bamboo user login");
133 | options.addOption("h", false, "Display this help");
134 |
135 | return options;
136 | }
137 |
138 | private static BambooYamlFileModel readAndValidateYamlFile(String path) {
139 |
140 | try {
141 | BambooYamlFileModel bambooFile;
142 |
143 | ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
144 | mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
145 |
146 | bambooFile = mapper.readValue(new File(path), BambooYamlFileModel.class);
147 |
148 | Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
149 |
150 | Set> violations = validator.validate(bambooFile);
151 | if (!violations.isEmpty()) {
152 | StringBuilder builder = new StringBuilder();
153 | violations.forEach(x -> builder.append(String.format("%s: %s%n", x.getPropertyPath(), x.getMessage())));
154 | throw new RuntimeException(String.format("Validation errors occurred:%n%s", builder.toString()));
155 | }
156 | return bambooFile;
157 | } catch (Exception e) {
158 | LOGGER.error("Exception: {}", e);
159 | throw new RuntimeException("Error reading YAML file: " + e.getMessage());
160 | }
161 | }
162 |
163 | private static void printHelp(Options options) {
164 | HelpFormatter formatter = new HelpFormatter();
165 | formatter.printHelp("reece-specs [options] ...", "options:", options, "");
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
19 | 4.0.0
20 |
21 |
22 | com.atlassian.bamboo
23 | bamboo-specs-parent
24 | 7.0.4
25 |
26 |
27 |
28 | au.com.reece.de
29 | bamboo-specs-reece
30 | 2.3.18
31 | jar
32 | Reece Bamboo Specs Runner
33 | Take YAML, transform to Bamboo Java, send to Bamboo
34 |
35 |
36 | 1.8
37 | ${java.version}
38 | ${java.version}
39 | 2.13.3
40 | 2.11.0
41 |
42 |
43 |
44 |
45 | com.atlassian.bamboo
46 | bamboo-specs-api
47 |
48 |
49 | com.atlassian.bamboo
50 | bamboo-specs
51 |
52 |
53 |
54 |
55 | com.fasterxml.jackson.core
56 | jackson-databind
57 | ${jackson.version}
58 |
59 |
60 | com.fasterxml.jackson.dataformat
61 | jackson-dataformat-yaml
62 | ${jackson.version}
63 |
64 |
65 |
66 | commons-cli
67 | commons-cli
68 | 1.4
69 |
70 |
71 |
72 |
73 | com.squareup.okhttp3
74 | okhttp
75 | 4.7.2
76 |
77 |
78 |
79 |
80 | org.jtwig
81 | jtwig-core
82 | 5.87.0.RELEASE
83 |
84 |
85 |
86 | javax.validation
87 | validation-api
88 | 2.0.1.Final
89 |
90 |
91 |
92 | org.apache.logging.log4j
93 | log4j-api
94 | ${log4j.version}
95 |
96 |
97 | org.apache.logging.log4j
98 | log4j-core
99 | ${log4j.version}
100 |
101 |
102 | org.apache.logging.log4j
103 | log4j-slf4j-impl
104 | ${log4j.version}
105 |
106 |
107 |
108 | org.glassfish
109 | javax.el
110 | 3.0.1-b11
111 |
112 |
113 | org.hibernate.validator
114 | hibernate-validator-cdi
115 | 6.1.5.Final
116 |
117 |
118 | org.hibernate.validator
119 | hibernate-validator
120 | 6.1.5.Final
121 |
122 |
123 |
124 | junit
125 | junit
126 | 4.13
127 | test
128 |
129 |
130 | commons-io
131 | commons-io
132 | 2.7
133 | test
134 |
135 |
136 | org.xmlunit
137 | xmlunit-core
138 | 2.7.0
139 | test
140 |
141 |
142 | org.mockito
143 | mockito-core
144 | 3.3.3
145 | test
146 |
147 |
148 |
149 |
150 |
151 |
152 | org.apache.maven.plugins
153 | maven-failsafe-plugin
154 |
155 | true
156 |
157 |
158 |
159 | org.apache.maven.plugins
160 | maven-shade-plugin
161 | 3.2.4
162 |
163 |
164 |
165 | package
166 |
167 | shade
168 |
169 |
170 |
171 |
172 |
174 | au.com.reece.de.bamboospecs.ReeceSpecs
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 | reece-distribute
187 |
188 |
189 | snapshots
190 | reecetech Artifactory-snapshots
191 | https://artifactory.reecenet.org:443/artifactory/libs-snapshot
192 |
193 |
194 | central
195 | reecetech Artifactory-releases
196 | https://artifactory.reecenet.org:443/artifactory/libs-release
197 |
198 |
199 |
200 |
201 |
202 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------
/src/main/java/au/com/reece/de/bamboospecs/models/TaskModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Reece Pty Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package au.com.reece.de.bamboospecs.models;
18 |
19 | import au.com.reece.de.bamboospecs.models.docker.DockerContainer;
20 | import au.com.reece.de.bamboospecs.models.docker.DockerStartCheck;
21 | import au.com.reece.de.bamboospecs.models.docker.PortMapping;
22 | import au.com.reece.de.bamboospecs.models.docker.VolumeMapping;
23 | import au.com.reece.de.bamboospecs.models.enums.InjectScopeType;
24 | import au.com.reece.de.bamboospecs.models.enums.TaskType;
25 | import com.atlassian.bamboo.specs.api.builders.AtlassianModule;
26 | import com.atlassian.bamboo.specs.api.builders.task.AnyTask;
27 | import com.atlassian.bamboo.specs.api.builders.task.Task;
28 | import com.atlassian.bamboo.specs.builders.task.*;
29 | import com.atlassian.bamboo.specs.model.task.TestParserTaskProperties;
30 | import com.fasterxml.jackson.databind.ObjectMapper;
31 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
32 | import com.google.common.base.Strings;
33 | import org.apache.commons.collections.CollectionUtils;
34 | import org.parboiled.common.StringUtils;
35 | import org.slf4j.Logger;
36 | import org.slf4j.LoggerFactory;
37 |
38 | import javax.validation.ConstraintViolation;
39 | import javax.validation.Validation;
40 | import javax.validation.Validator;
41 | import javax.validation.constraints.NotEmpty;
42 | import javax.validation.constraints.NotNull;
43 | import java.io.File;
44 | import java.io.IOException;
45 | import java.nio.file.Path;
46 | import java.nio.file.Paths;
47 | import java.util.HashMap;
48 | import java.util.List;
49 | import java.util.Map;
50 | import java.util.Set;
51 |
52 | public class TaskModel extends DomainModel {
53 | private static final Logger LOGGER = LoggerFactory.getLogger(TaskModel.class);
54 |
55 | public String include;
56 |
57 | public String yamlPath;
58 |
59 | public TaskType type;
60 |
61 | // Used by all task types
62 | public String description;
63 |
64 | // Used by: SCRIPT
65 | public String body;
66 |
67 | // Used by: JUNIT
68 | public String resultFrom;
69 |
70 | // Used by: VCS
71 | public List repositories;
72 | public boolean cleanCheckout = false;
73 | public boolean defaultRepository = false;
74 | public String cloneDirectory;
75 |
76 | // Used by: DOCKER and SCRIPT
77 | public String workingDirectory;
78 |
79 | // Used by: DOCKER
80 | public String image;
81 | public boolean detach = false;
82 | public DockerStartCheck serviceStartCheck;
83 | public String environmentVariables;
84 | public DockerContainer container;
85 | public VolumeMapping[] volumeMappings;
86 | public PortMapping[] portMappings;
87 | public String cmdLineArguments;
88 |
89 | // legacy attributes from DOCKER
90 | public String command;
91 | // end legacy
92 |
93 | // Used by: INJECT
94 | public String propertiesFile;
95 | public String namespace;
96 | public InjectScopeType scope = InjectScopeType.RESULT;
97 |
98 | // Used by: CUCUMBER_REPORT
99 | public String reportPath;
100 |
101 | // Used by: SPECIFIC_ARTEFACTS
102 | public List artifacts;
103 |
104 | public Task asTask() {
105 | if (include != null && !include.isEmpty()) {
106 | Path includedYaml = Paths.get(this.yamlPath, this.include);
107 | return TaskModel.fromYAML(includedYaml.toString()).asTask();
108 | } else {
109 | if (description == null || description.isEmpty()) {
110 | throw new RuntimeException("Tasks must have a description");
111 | }
112 | switch (this.type) {
113 | case VCS:
114 | return getVersionControlTask();
115 | case SCRIPT:
116 | return getScriptTask();
117 | case JUNIT:
118 | return getJunitTask();
119 | case TESTNG:
120 | return getTestngTask();
121 | case DOCKER:
122 | return getDockerTask();
123 | case CLEAN:
124 | return new CleanWorkingDirectoryTask();
125 | case ARTEFACT:
126 | return getArtefactDownloadTask();
127 | case SPECIFIC_ARTEFACTS:
128 | return getSpecificArtefactsDownloadTask();
129 | case INJECT:
130 | return getInjectTask();
131 | case CUCUMBER_REPORT:
132 | return getCucumberReportTask();
133 | default:
134 | // shouldn't actually be possible, given we load via enum
135 | throw new RuntimeException("Unexpected 'type' value from yaml " + this.type);
136 | }
137 | }
138 | }
139 |
140 | private Task getCucumberReportTask() {
141 | if (StringUtils.isEmpty(reportPath)) {
142 | throw new RuntimeException("Missing 'reportPath' value from yaml for CUCUMBER_REPORT");
143 | }
144 |
145 | Map configuration = new HashMap<>();
146 | configuration.put("testPattern", reportPath);
147 |
148 | return new AnyTask(new AtlassianModule("com.hindsighttesting.behave.cucumber-bamboo-plugin:cucumberReportTask"))
149 | .description(description)
150 | .configuration(configuration);
151 | }
152 |
153 | private Task getInjectTask() {
154 | InjectVariablesTask task = new InjectVariablesTask().description(this.description);
155 | if (this.namespace == null || this.namespace.isEmpty()) {
156 | throw new RuntimeException("Missing 'namespace' value from yaml for INJECT");
157 | }
158 | if (this.propertiesFile == null || this.propertiesFile.isEmpty()) {
159 | throw new RuntimeException("Missing 'propertiesFile' value from yaml for INJECT");
160 | }
161 | task.namespace(this.namespace).path(this.propertiesFile);
162 | switch (this.scope) {
163 | case LOCAL:
164 | return task.scopeLocal();
165 | case RESULT:
166 | return task.scopeResult();
167 | default:
168 | throw new RuntimeException("Unexpected 'scope' value from yaml " + this.scope);
169 | }
170 | }
171 |
172 | private Task getVersionControlTask() {
173 | VcsCheckoutTask task = new VcsCheckoutTask().description(this.description);
174 | if (this.defaultRepository) {
175 | if (!Strings.isNullOrEmpty(this.cloneDirectory)) {
176 | task.checkoutItems(new CheckoutItem().defaultRepository().path(this.cloneDirectory));
177 | } else {
178 | task.checkoutItems(new CheckoutItem().defaultRepository());
179 | }
180 | }
181 | if (this.repositories != null) {
182 | for (RepositoryModel vcs : this.repositories) {
183 | task.checkoutItems(vcs.asCheckoutItem());
184 | }
185 | }
186 | if (this.cleanCheckout) {
187 | task.cleanCheckout(true);
188 | }
189 | return task;
190 | }
191 |
192 | private Task getScriptTask() {
193 | if (this.body == null) {
194 | throw new RuntimeException("Missing 'body' value from yaml for SCRIPT");
195 | }
196 | ScriptTask scriptTask = new ScriptTask().description(this.description).inlineBody(this.body);
197 | if (this.workingDirectory != null) {
198 | scriptTask.workingSubdirectory(this.workingDirectory);
199 | }
200 | return scriptTask;
201 | }
202 |
203 | private Task getJunitTask() {
204 | if (this.resultFrom == null) {
205 | throw new RuntimeException("Missing 'resultFrom' value from yaml for JUNIT");
206 | }
207 | return new TestParserTask(TestParserTaskProperties.TestType.JUNIT)
208 | .description(this.description)
209 | .resultDirectories(this.resultFrom);
210 | }
211 |
212 | private Task getTestngTask() {
213 | if (this.resultFrom == null) {
214 | throw new RuntimeException("Missing 'resultFrom' value from yaml for TESTNG");
215 | }
216 | return new TestParserTask(TestParserTaskProperties.TestType.TESTNG)
217 | .description(this.description)
218 | .resultDirectories(this.resultFrom);
219 | }
220 |
221 | private Task getArtefactDownloadTask() {
222 | return new ArtifactDownloaderTask().artifacts(new DownloadItem().allArtifacts(true));
223 | }
224 |
225 | private Task getSpecificArtefactsDownloadTask() {
226 | if (CollectionUtils.isEmpty(artifacts)) {
227 | throw new RuntimeException("Missing 'artifacts' value from yaml for SPECIFIC_ARTEFACTS");
228 | }
229 |
230 | // convert model to download item.
231 | DownloadItem[] items = artifacts.stream()
232 | .map(DownloadArtifactModel::asDownloadItem)
233 | .toArray(DownloadItem[]::new);
234 |
235 | // create the task with specific download items.
236 | return new ArtifactDownloaderTask().description(description).artifacts(items);
237 | }
238 |
239 | private Task getDockerTask() {
240 | if (this.image == null) {
241 | throw new RuntimeException("DOCKER tasks require 'image' to be set");
242 | }
243 |
244 | // note the serviceURLPattern comes from the Bamboo docs and spec dump and doesn't
245 | // appear to be sensible to override.
246 | DockerRunContainerTask docker = new DockerRunContainerTask().imageName(this.image)
247 | .description(this.description)
248 | .serviceURLPattern("http://localhost:${docker.port}");
249 |
250 | // legacy options
251 | if (this.command != null) docker.containerCommand(this.command);
252 | if (this.workingDirectory != null) docker.containerWorkingDirectory(this.workingDirectory);
253 | // end legacy
254 |
255 | if (this.container != null) {
256 | this.container.applyConfig(docker);
257 | }
258 |
259 | if (this.detach) {
260 | if (this.container == null || this.container.name == null) {
261 | throw new RuntimeException("DOCKER detached tasks require 'container' -> 'name' to be set");
262 | }
263 | docker.detachContainer(true);
264 | if (this.serviceStartCheck != null) {
265 | this.serviceStartCheck.applyConfig(docker);
266 | }
267 | }
268 |
269 | if (this.environmentVariables != null) docker.environmentVariables(this.environmentVariables);
270 |
271 | if (this.cmdLineArguments != null) docker.additionalArguments(this.cmdLineArguments);
272 |
273 | docker.clearPortMappings();
274 | if (this.portMappings != null) {
275 | for (PortMapping port : this.portMappings) {
276 | docker.appendPortMapping(port.local, port.container);
277 | }
278 | }
279 |
280 | docker.clearVolumeMappings();
281 | if (this.volumeMappings != null) {
282 | for (VolumeMapping volume : this.volumeMappings) {
283 | docker.appendVolumeMapping(volume.local, volume.container);
284 | }
285 | }
286 | return docker;
287 | }
288 |
289 | public static TaskModel fromYAML(String filename) {
290 | LOGGER.info("Parsing task model YAML {}", filename);
291 |
292 | File yaml = new File(filename);
293 |
294 | ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
295 | Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
296 |
297 | TaskModel included;
298 | try {
299 | included = mapper.readValue(yaml, TaskModel.class);
300 | Set> violations = validator.validate(included);
301 | if (!violations.isEmpty()) {
302 | violations.forEach(x -> LOGGER.error("{}: {}", x.getPropertyPath(), x.getMessage()));
303 | throw new RuntimeException("Error parsing included task from " + filename);
304 | }
305 | } catch (IOException e) {
306 | throw new RuntimeException("Error parsing included task from " + filename + "due to error " + e.getMessage(), e);
307 | }
308 |
309 | return included;
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bamboo Plan and Deployment Configuration
2 |
3 | [](https://travis-ci.org/reecetech/bamboo-specs)
4 |
5 | This project provides a tool that uses YAML files to specify
6 | how Bamboo plans and deployment projects should be configured.
7 |
8 | ## Sample configuration files
9 |
10 | See the `specifications` directory for sample files (patersoa TODO: add more examples)
11 |
12 | ## Credentials and Authentication
13 |
14 | Before running this program you need to configure an admin user
15 | to the user the program will run as (ie. your Bamboo credentials)
16 | You do this by creating a `.credentials` file with the following
17 | contents:
18 |
19 | username=
20 | password=
21 |
22 | Don't check this into a repository.
23 |
24 | ## Building and Running
25 |
26 | Build the code with:
27 |
28 | mvn package
29 |
30 | Run with:
31 |
32 | java -jar target/bamboo-specs-reece-2.0.0.jar permissions.yaml plan.yaml deployment-project.yaml
33 |
34 | You can test your YAML using the -t switch passed to any of those commands, for example:
35 |
36 | java -jar target/bamboo-specs-reece-2.0.0.jar -t plan.yaml
37 |
38 | This will just parse the YAML and not deploy it to Bamboo.
39 |
40 | The commands all accept multiple yaml files to process:
41 |
42 | java -jar target/bamboo-specs-reece-2.0.0.jar configs/plan-*.yaml
43 |
44 | ## Authorizing Bamboo to access Stash
45 |
46 | Visit this URL: `https://${bamboo-server}/bamboo/admin/configureLinkedRepositories!doDefault.action`
47 |
48 | and then click `Add Repository` and select `BitBucket Server/Stash`. If you haven't authorized it already, you will be prompted to enter your credentials.
49 |
50 | ## Java SSL keystore fix
51 |
52 | If you get this error (or anything mentioning a certificate) when running the jar files, you need to add your corporate CA cert to your Java keystore:
53 |
54 | `INFO [BambooServer] An error occurred while publishing plan AS-BK8SD`
55 |
56 | On Ubuntu:
57 | ----------
58 | Visit https://bamboo.reecenet.org in Chrome
59 | Use developer tools - Security - View Certificate - details tab - export
60 | This will save a copy of the public certificate to a file
61 | (vicpjdt01.reecenet.org in the example below)
62 | Import that file using:
63 | keytool -import -alias vicpjdt01.reecenet.org -keystore cacerts \
64 | -trustcacerts -file ~dev/vicpjdt01.reecenet.org
65 | This will prompt for a password; the default is "changeit"
66 |
67 | On Windows:
68 | -----------
69 | keytool -importcert -alias vicpjdt01.reecenet.org \
70 | -file vicpjdt01.reecenet.org.cer -keystore "C:\Program Files (x86)\Java\jre1.8.0_131\lib\security\cacerts"
71 |
72 | On macOS:
73 | ------------
74 | Download the certificates `reecenet-ca.crt` and `reecenet-intermediate.crt` from https://stash.reecenet.org/projects/DBI/repos/rhel7_java_base/browse and place them under `/tmp`.
75 |
76 | Find the Java home:
77 | $ /usr/libexec/java_home
78 |
79 | Then import the cert:
80 | $ cd /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/security
81 | $ sudo cp cacerts cacerts.orig
82 | $ sudo keytool -trustcacerts -keystore cacerts -noprompt -alias reecenet-base -importcert -file /tmp/reecenet-ca.crt -storepass changeit
83 | $ sudo keytool -trustcacerts -keystore cacerts -noprompt -alias reecenet-intermediate -importcert -file /tmp/reecenet-intermediate.crt -storepass changeit
84 |
85 | ## Controlling Permissions
86 |
87 | Create a permissions.yaml file:
88 |
89 | specType: permissions
90 | bambooServer: https://bamboo.reecenet.org/bamboo
91 | projects:
92 | - plans: [BST-ST, SPAM-IT]
93 | permissions:
94 | - groups: [Cyborg_Team]
95 | users: [islaa, tobind]
96 | grant: [VIEW, EDIT, BUILD, CLONE, ADMIN]
97 | - plans: [BST-ST]
98 | permissions:
99 | - users: [dooleyj]
100 | grant: [VIEW]
101 | - plans: [SPAM-IT]
102 | permissions:
103 | - allLoggedInUsers: true
104 | grant: [VIEW]
105 | deployments:
106 | - name: Diary Notes Python Shared Service
107 | permissions:
108 | - users: [dooleyj, poultonj]
109 | grant: [VIEW, EDIT]
110 | environments:
111 | - names: [Production (AU + NZ), TEST AU, TEST NZ]
112 | permissions:
113 | - groups: [Cyborg_Team]
114 | grant: [VIEW, EDIT, BUILD]
115 | - name: Customer Prices Service
116 | permissions:
117 | - users: [dooleyj, poultonj]
118 | grant: [VIEW, EDIT]
119 | environments:
120 | - names: [Production (AU + NZ)]
121 | permissions:
122 | - groups: [Cyborg_Team]
123 | grant: [VIEW, EDIT, BUILD]
124 |
125 | There can be many entries in the permissions: list, each of which
126 | specifies groups and/or users and permissions to grant to them.
127 |
128 | The projects list contains plans identified by key pairs (project key, plan
129 | key) so the first list has the ST plan in the BST project, and the IT plan
130 | in the SPAM project. These key pairs are at the end of the URL when
131 | viewing a project's plan, eg:
132 |
133 | https://bamboo.reecenet.org/bamboo/browse/DNSS-DNPSM
134 |
135 | The optional deployments list contains deployments and environments identified by
136 | their label in the Bamboo interface. Note that permissions granted to a
137 | deployment are only for administrating the deployment project settings in
138 | Bamboo and do *not* affect the access controls for each of the environments.
139 |
140 | Each permission will be granted to each group and user in each plan, deployment
141 | project or environment for a given permissions entry. So for the first group
142 | above, the complete set of permissions will be granted to the `Cyborg_Team`
143 | group and users `islaa` and `tobind` in the `BST-ST` and `SPAM-IT` plans.
144 |
145 | The allowed permissions for each section are:
146 |
147 | * **plans**: VIEW, EDIT, BUILD, CLONE, ADMIN
148 | * **deployments**: VIEW, EDIT
149 | * **environments**: VIEW, EDIT, BUILD
150 |
151 | The admin user used to make the changes (see Credentials and Authentication) is
152 | hard-coded to be granted admin user permission regardless of the other settings
153 | in the permissions yaml, to prevent that user from having that permission
154 | removed (which would break the program).
155 |
156 |
157 | ## Build and Test Plans
158 |
159 | Plans have a lot more options. The required minimum is:
160 |
161 | specType: plan
162 | bambooServer: https://bamboo.reecenet.org/bamboo
163 | projectKey: BST
164 | projectName: Bamboo Spec Testing
165 | planKey: ST
166 | planName: Spec Testing
167 | description: This is a test plan for bamboo specs
168 | maximumConcurrentBuilds: 1
169 |
170 | If the Plan or Project do not exist in Bamboo they will be created, so please
171 | double-check that the `projectKey` and `planKey` are correct.
172 |
173 | The rest of the configuration is all optional chunks, though some will depend
174 | on others (VCS tasks would require a repository, for example).
175 |
176 | If you have arbitrary variables stored on a plan you may set them as key-value
177 | pairs like so:
178 |
179 | variables:
180 | major_version_number: 1
181 | target_name: bamboo-spec-testing
182 |
183 | Variables defined here (and others defined by Bamboo for you) may be reference
184 | in SCRIPT task body texts using `${bamboo.major_version_number}` or
185 | `${bamboo.target_name}` using the above example settings.
186 |
187 | You can also add labels to your build plan by simply adding a list of strings
188 | you would like your build tagged with:
189 |
190 | labels:
191 | - awesome
192 | - very_cool
193 |
194 | ### Source Repositories
195 |
196 | If there are repositories used then include as either linked repositories
197 | (shared between plans):
198 |
199 | linkedRepositories: [Bamboo Spec Test Project, Other Repository]
200 |
201 | The linked repository is typically added when a plan is created. Alternatively
202 | you can use a locally (to this plan) defined repositories:
203 |
204 | repositories:
205 | - name: Bamboo Spec Test Project
206 | projectKey: SAN
207 | repositorySlug: bamboo-spec-test-project
208 | branch: development
209 | triggerPattern: /files/i/care/about/*
210 | shallowClone: false
211 | - name: PACT Specification
212 | gitURL: https://github.com/pact-foundation/pact-specification.git
213 | branch: version-1.1
214 | path: pact-spec-version-1.1
215 |
216 | So the two types of repositories currently supported are:
217 |
218 | 1. A repository in the Reece Stash instance. It's identified by the `projectKey`
219 | and `repositorySlug` from the repository URL like so:
220 |
221 | https://stash.reecenet.org/projects/\/repos/\/browse
222 |
223 | The "branch" is optional (default is "master"). Additionally, you can specify a 'trigger pattern' that will cause the build to fire only if the changed files match the pattern (a regex).
224 |
225 | You can also specify whether or not the clone operation is 'shallow'. By default, the clone is shallow.
226 |
227 | 2. An arbitrary git repository identified by `gitURL`. It must specify a `path`
228 | that the repository will be cloned to, and optionally a `branch`.
229 |
230 | Plan branches are local configurations based on branches in the repository and
231 | the strategy for synchronising the two are controlled with:
232 |
233 | branchManagement:
234 | createStrategy: MANUALLY
235 |
236 | The creation strategy is one of: `MANUALLY`, `ON_PULL_REQUEST`, `ON_NEW_BRANCH`
237 | or `ON_BRANCH_PATTERN`. The last will create on new branches matching a name
238 | pattern regular expression:
239 |
240 | branchManagement:
241 | createStrategy: ON_BRANCH_PATTERN
242 | branchPattern: feature/.*
243 |
244 | The `issueLinkingEnabled` option enables automatic linking of the plan branch
245 | to the Jira issue related to the repository branch, which is enabled by default.
246 | Cleaning up plan branches is defaulted to 7 days after the repository branch is
247 | deleted. The default is to never clean up branches that are simply inactive.
248 | These options may be modified in the `branchManagement` section:
249 |
250 | branchManagement:
251 | issueLinkingEnabled: false
252 | delayCleanAfterDelete: 2
253 | delayCleanAfterInactivity: 60
254 |
255 | ### Triggers
256 |
257 | Plans may also have a `triggers` section to indicate the specific circumstances
258 | in which they are to be triggered (that is, their tasks should be executed),
259 | say running unit tests after commits to the stash repository:
260 |
261 | triggers:
262 | - type: AFTER_STASH_COMMIT
263 | description: Trigger from stash changes
264 |
265 | Or perhaps trigger a deploy from a successful build:
266 |
267 | triggers:
268 | - type: AFTER_SUCCESSFUL_BUILD_PLAN
269 | description: Deploy main plan branch (master)
270 |
271 | You may also trigger only off certain non-master branches:
272 |
273 | triggers:
274 | - type: AFTER_SUCCESSFUL_BUILD_PLAN
275 | branch: development
276 | description: Deploy development branch
277 |
278 | Or scheduled using a variety of periods:
279 |
280 | triggers:
281 | - type: SCHEDULED
282 | description: Test every 4 hours
283 | everyNumHours: 4
284 | dailyAt: 5:00
285 | weeklyAt: Saturday 12:59
286 | montlyAt: 15 12:59
287 | cron: "0 0/30 9-19 ? * MON-FRI"
288 |
289 | If the plan has dependent plans which are to be triggered when
290 | this plan completes they may be specified (as "dependencies"):
291 |
292 | dependencies:
293 | requiresPassing: true
294 | plans: [USRSRV-UPSDB]
295 |
296 | If there are no dependencies you may leave this section out, though if
297 | the plan *previously had dependencies* you will need to explicitly clear
298 | them with:
299 |
300 | dependencies:
301 | none: true
302 |
303 | If you don't then you'll get an error "Plan import is blocked to prevent
304 | deleting your plan dependencies silently."
305 |
306 | ### Notifications
307 |
308 | Notifications on plan completion are supported:
309 |
310 | notifications:
311 | - when: PLAN_COMPLETED
312 | slack: https://hooks.slack.com/services/...the rest of the URL...|#cyborg-dev
313 | recipientGroups: [Cyborg_Team]
314 | recipientUsers: [dooleyj, poultonj]
315 | responsibleUser: "true"
316 |
317 | At least one of the notification targets is required: `slack`,
318 | `recipientGroups`, `responsibleUser` or `recipientUsers`. The `when` values are `PLAN_COMPLETED`,
319 | `PLAN_FAILED`, `STATUS_CHANGED`, DEPLOYMENT_FAILED` and `DEPLOYMENT_FINISHED` which mirror the options of the
320 | same name in the Bamboo UI.
321 |
322 | ### Stages, Jobs and Tasks
323 |
324 | Your plan may have multiple stages, which each have jobs, and each job may have
325 | tasks and *final* tasks (tasks to run even if the other tasks fail).
326 |
327 | Stages and jobs may be defined:
328 |
329 | stages:
330 | - name: Default Stage
331 | jobs:
332 | - name: Run Tests
333 | key: JOB1
334 | description: Run Python Unit Tests
335 | requirements:
336 | - name: system.docker.executable
337 | - name: DOCKER
338 | - name: LINUX
339 | artifacts:
340 | - name: PACT Contracts
341 | pattern: "**"
342 | location: pacts
343 | - name: Coverage Report
344 | pattern: "**"
345 | location: htmlcov
346 | - name: Docker Image
347 | pattern: built-image.docker
348 | location: .
349 | shared: true
350 |
351 | The job key is arbitrary and unique inside a plan. Requirements and artifacts are optional.
352 |
353 | The requirements are restrictions on which Bamboo agents may be used to run the plan's jobs.
354 | The actual list of requirements possible is available in the Bamboo UI, though the precise
355 | key to be used in the list above is unclear for the requirements build into Bamboo. For
356 | example, in the Bamboo UI you may select the built-in "Docker" requirement, which in the
357 | list above is "system.docker.executable". The UI also lists "DOCKER" which is represented
358 | in the list above with the same name. I recommend adding the requirement through the UI
359 | and using the "View plan as Bamboo Specs" option under Plan Configuration Actions menu to
360 | determine the actual string to use in the requirements list in YAML.
361 |
362 | Artifacts must be `shared: true` if you wish to use them in other jobs. You must
363 | then subscribe to the artifact from the other job using `artifactSubscriptions`:
364 |
365 | stages:
366 | - name: Second Stage
367 | jobs:
368 | - name: Run More Tests
369 | artifactSubscriptions:
370 | - name: Docker Image
371 | destination: .
372 |
373 | A job may then have a list of tasks:
374 |
375 | stages:
376 | - name: Default Stage
377 | jobs:
378 | - name: Run Tests
379 | ...
380 | tasks:
381 | - type: VCS
382 | description: Checkout Default Repository
383 | cleanCheckout: true
384 | - type: SCRIPT
385 | description: Build docker image
386 | body: |
387 | set -ex
388 | scripts/test_image.sh bamboo/${bamboo.target_name}
389 | - type: SCRIPT
390 | description: Run tests
391 | body: |
392 | set -ex
393 | scripts/run_tests.sh
394 |
395 | Here you can see we refer to the bamboo variable we defined way up above so that
396 | the script body may be the same across multiple projects.
397 |
398 | #### VCS Task
399 |
400 | The VCS task has a number of options. By default it will check out the default
401 | repository for the plan. If you wish to check out other repositories you may list
402 | them (and optionally include the default repository also):
403 |
404 | - type: VCS
405 | description: Checkout All Repositories
406 | defaultRepository: true
407 | repositories:
408 | - name: Running Man
409 | - name: Running Man Properties
410 | path: properties
411 | cleanCheckout: true
412 |
413 | The default repository will always be checked out first (if used), and then the other
414 | repositories in the order specified.
415 |
416 | If you wish to force a clean checkout of the repositories on or off use `cleanCheckout`.
417 |
418 | #### SCRIPT Tasks
419 |
420 | These are pretty simple, just bash scripts that contain a body to run.
421 |
422 | If you have multiple repositories be setup, then you will need subWorkingDirectory to support your script tasks.
423 | Here is the example
424 |
425 | - type: SCRIPT
426 | description: Run unit tests
427 | body: |
428 | echo "Do your actions here."
429 | workingDirectory: your_sub_directory
430 |
431 | #### DOCKER Tasks
432 |
433 | Currently only the *run* docker task is supported. It requires the *image* property
434 | to be specified, but also allows all the other options:
435 |
436 | - type: DOCKER
437 | description: Run unit tests
438 | image: dockerrepo.reecenet.org:4433/cyborg/tox-tests
439 | environmentVariables: JAVA_OPTS="-Xmx256m -Xms128m"
440 | cmdLineArguments: -u 1000
441 | container:
442 | workingDirectory: /app
443 | environmentVariables: PACT_DIR=/app/pacts
444 | command: tox
445 | volumeMappings:
446 | - local: ${bamboo.working.directory}
447 | container: /app
448 |
449 | The `cmdLineAguments` are additional arguments for the "docker run" command. Note the
450 | distinction between environment variables set *outside* the docker container and
451 | those set *inside* the container.
452 |
453 | Also supported is running docker containers in the background with port mappings:
454 |
455 | - type: DOCKER
456 | description: Run unit tests
457 | image: dockerrepo.reecenet.org:4433/cyborg/some-server
458 | detach: true
459 | container:
460 | name: detached-server
461 | workingDirectory: /app
462 | portMappings:
463 | - local: 8080
464 | container: 8001
465 | serviceStartCheck:
466 | url: http://localhost:${docker.port}
467 | timeout: 120
468 |
469 | Note that the container -> name property is required for detached containers. The
470 | docker.port variable will provide the first exposed port for creating the service
471 | check URL.
472 |
473 | #### INJECT Tasks
474 |
475 | While running tasks you may write values to a properties file which the INJECT
476 | task makes available to other tasks. To specify the file, use:
477 |
478 | - type: INJECT
479 | description: Store variables for use in other tasks
480 | namespace: inject
481 | propertiesFile: inject-properties.ini
482 | scope: RESULT
483 |
484 | The file must exist when this task is run and uses a 'key=value' format.
485 | You must provide a relative path to the property file. The values will be available
486 | in the "bamboo." variable namespace, so given this properties file:
487 |
488 | key=value
489 | version=1.2.3
490 |
491 | These values will be available in other tasks (scripts, etc) as the variables
492 | "${bamboo.inject.key}" and "${bamboo.inject.version}".
493 |
494 | The variables will be discarded at the end of the Job if the scope is "LOCAL" and
495 | retained for other Jobs if the scope is "RESULT" (the default).
496 |
497 | #### CUCUMBER_REPORT Tasks
498 |
499 | This task is used to showing the cucumber test json report.
500 |
501 | - type: CUCUMBER_REPORT
502 | description: Cucumber JSON test report
503 | reportPath: "test/cucumber./reports/json/*.json"
504 |
505 | #### ARTIFACTORY Tasks
506 |
507 | This task is used to deploy a generic artefact to Artifactory
508 |
509 | - type: ARTIFACTORY
510 | uploadSpec: |
511 | { * JSON upload spec here * }
512 |
513 | For reference on building the 'upload spec', please consult the Artifactory documentation: https://www.jfrog.com/confluence/display/RTF/Using+File+Specs
514 |
515 | #### Artefact Download
516 |
517 | - type: ARTEFACT
518 | description: Download Artefacts
519 |
520 | This will download _all_ artefacts generated by the build into the current directory.
521 |
522 | #### Specific Artefacts Download
523 |
524 | - type: SPECIFIC_ARTEFACTS
525 | description: This is the description section (~˘▾˘)~
526 | artifacts:
527 | - name: Helm Package
528 | path: .
529 | - name: Coverage Report
530 | path: .
531 |
532 | This will download a specific list of artefacts generated by the build into the current directory.
533 | Also the name of artifact has to be matched to the name defined in source plan.
534 |
535 | ### Final Tasks
536 |
537 | Final tasks are tasks that are always run after the other tasks, regardless of whether
538 | they were successful. These could be cleanup tasks, or more commonly including a
539 | JUnit parser to parse the results of the tests which may have failed:
540 |
541 | finalTasks:
542 | - type: JUNIT
543 | description: Include XML test results
544 | resultFrom: "**/unittest-report/xml/*.xml"
545 |
546 | ## Reusable Jobs
547 |
548 | If you have the same Job run across many different plans you can craft a YAML file that
549 | contains *just* the Job specification, for example "include/library-unit-tests.yaml":
550 |
551 | name: Run Library Unit Tests
552 | key: UT
553 | description: Run tox unit tests and check setup.py version against existing git tags
554 | requirements:
555 | - name: system.docker.executable
556 | tasks:
557 | - type: VCS
558 | ...
559 |
560 | And then in your plan YAML file you may include that (noting that you may also declare
561 | other plan-specific jobs):
562 |
563 | stages:
564 | - name: Default Stage
565 | jobs:
566 | - include: include/library-unit-tests.yaml
567 | - name: Build package
568 | key: BUILD
569 | description: Build Python package
570 | requirements:
571 | ...
572 |
573 | In this example the include file is in a separate subdirectory - this makes it easier
574 | to use globbing to process multiple files in a single directory, like:
575 |
576 | java -jar target/bamboo-specs-reece-1.0.5.jar plan bamboo-configs/Cyborg/unit-tests/*.yaml
577 |
578 |
579 | ## Deployment Projects
580 |
581 | Deployment projects look a lot like build and test plans, and even share some of the
582 | same sections, but do have a different preamble and structure.
583 |
584 | ### Top Level Settings
585 |
586 | At the top of the file you need to identify the deployment by *name*, and then the
587 | build plan that it belongs to:
588 |
589 | specType: deployment
590 | bambooServer: https://bamboo.reecenet.org/bamboo
591 | name: Diary Notes Python Shared Service
592 | buildProject: DNSS
593 | buildPlan: DNPSDB
594 | description: This is a deployment plan for the Diary Notes Python Shared Service
595 |
596 | You should also define the release naming scheme:
597 |
598 | releaseNaming:
599 | pattern: ${bamboo.version_major_number}.${bamboo.buildNumber}
600 | retainNamingStrategyForBranches: false
601 |
602 | If you would like to set variables across all environments you can set variables in
603 | the preamble:
604 |
605 | variables:
606 | target_name: diary-notes-service
607 |
608 | This will have the effect of setting the variable in each of the environments (Bamboo
609 | does not offer variables at this level).
610 |
611 |
612 | ### Permissions
613 |
614 | Deployment project permissions may be set in this file with a section at the top
615 | level which has the same basic structure as the previous permissions settings (users,
616 | groups, grants), just that it has two sections:
617 |
618 | permissions:
619 | project:
620 | users: [dooleyj, poultonj]
621 | groups: [Cyborg_Team]
622 | grant: [VIEW, EDIT]
623 | environment:
624 | users: [dooleyj, poultonj]
625 | groups: [Cyborg_Team]
626 | grant: [VIEW, EDIT, BUILD]
627 |
628 | The first section applies to the the project itself, and the second applies to **all**
629 | environments in the project. There is currently no support for per-environment
630 | permissions in this file.
631 |
632 |
633 | ### Environments Structure
634 |
635 | This is a list of environments that you will deploy to. Each
636 | environment will have a name and description followed by requirements, notifications,
637 | variables and tasks that are constructed exactly the same as in build plans. So for example:
638 |
639 | environments:
640 | - environment: Production (AU + NZ)
641 | description: Deployment plan for the Diary Notes Python Shared Service to production
642 | requirements:
643 | - name: system.docker.executable
644 | notifications:
645 | - when: DEPLOYMENT_FINISHED
646 | slack: https://hooks.slack.com/services/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|#cyborg-dev
647 | variables:
648 | deployment_script: cutover.py
649 | tasks:
650 | - type: VCS
651 | description: Running Man
652 | repositories:
653 | - name: Running Man
654 | - name: Running Man Properties
655 | path: properties
656 | - type: SCRIPT
657 | description: Cutover Blue to Green - Training
658 | body: |
659 | ${deployment_script} ${bamboo.target_name} training_nz ${bamboo.version_major_number}.${bamboo.buildNumber}
660 | ${deployment_script} ${bamboo.target_name} training_au ${bamboo.version_major_number}.${bamboo.buildNumber}
661 | - type: SCRIPT
662 | description: Cutover Blue to Green - Production
663 | body: |
664 | ${deployment_script} ${bamboo.target_name} prod_au ${bamboo.version_major_number}.${bamboo.buildNumber}
665 | ${deployment_script} ${bamboo.target_name} prod_nz ${bamboo.version_major_number}.${bamboo.buildNumber}
666 | triggers:
667 | - type: AFTER_SUCCESSFUL_BUILD_PLAN
668 | description: Deploy main plan branch (master)
669 |
670 |
671 | ### Using Included Environments
672 |
673 | If you have the same environments appearing in multiple deployments you may save them off in a
674 | separate YAML file (say, `environments.yaml`) which has the exact structure of the above
675 | environments structure sample (ie. `environments:` at the top level) and then each
676 | of the named environments may be included in your deployment project yaml like so:
677 |
678 | includeEnvironments:
679 | from: environments.yaml
680 | environments: [
681 | Production (AU + NZ),
682 | POS1 TEST AU, POS1 TEST NZ, POS2 TEST AU, POS2 TEST NZ,
683 | POS2 UAT AU, POS2 UAT NZ
684 | ]
685 |
686 | ### Using included tasks inside environments
687 |
688 | If you have multiple environments specified and all or some of them are using the same set of tasks
689 | you can make use of `includedTasks`. Specify the set of tasks in a separate yaml file and then specify the reference
690 | in the environment. Note you can have both the `tasks` and `includedTasks` property specified, the `tasks` will be added first,
691 | then the included tasks.
692 |
693 | Specify tasks in separate yaml file:
694 |
695 | - type: CLEAN
696 | description: Clean working directory
697 | - type: ARTEFACT
698 | description: Download Helm chart
699 | - type: VCS
700 | description: Git Checkout
701 | repositories:
702 | - name: oyster-scripts
703 | path: oyster-scripts
704 |
705 | Use included tasks like this:
706 |
707 | - environment: trstst05
708 | description: Inventory UAT NZ
709 | requirements:
710 | - name: pegasus
711 | notifications:
712 | - when: DEPLOYMENT_FAILED
713 | slack: https://hooks.slack.com/services/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
714 | variables:
715 | k8s_cluster: non-production-internal-cluster
716 | tasks:
717 | - type: CLEAN
718 | description: Clean working directory
719 | # Path is relative to the root yaml file
720 | includedTasks: ../include/deployment-tasks.yaml
721 | triggers:
722 | - type: AFTER_SUCCESSFUL_BUILD_PLAN
723 | description: Deploy development branch
724 |
725 | Note that the path for the included tasks is relative to the root yaml file, in this case the actual deployment template yaml.
726 |
727 | ## Version History
728 |
729 | 2.3.18
730 |
731 | Add support for retaining the release naming strategy for branches.
732 | Commonly known as The Checkbox of Salvation.
733 |
734 | 2.3.17
735 |
736 | Add support for including tasks, remove support for Artifactory
737 |
738 | 2.3.16
739 |
740 | Upgrade to Bamboo 7.0.4
741 |
742 | 2.3.15
743 |
744 | Add support to include tasks for multiple deployment environments to reduce the repeating task entries in the environments specified inside a deployment template.
745 | Works for both inline environments and included environments inside the deployment template.
746 |
747 | 2.3.14
748 |
749 | Log errors to STDOUT
750 |
751 | 2.3.13
752 |
753 | Add support for customising maximum concurrent builds
754 |
755 | 2.3.12
756 |
757 | Add support for SPECIFIC_ARTEFACTS type which supports optional artifact download
758 |
759 | 2.3.11
760 |
761 | Add support for submodules in repo settings
762 |
763 | 2.3.10
764 |
765 | Add support for cucumber test JSON report task
766 |
767 | 2.3.9
768 |
769 | Add support for 'responsible user' notification
770 |
771 | 2.3.8
772 |
773 | Upgrade Bamboo to 6.10.4
774 |
775 | 2.3.9
776 |
777 | Add support for 'responsible user' notification
778 |
779 | 2.3.8
780 |
781 | Upgrade Bamboo to 6.10.4
782 |
783 | 2.3.7
784 |
785 | Add support for checking out into a different directory
786 |
787 | 2.3.6
788 |
789 | Add Apache license
790 |
791 | 2.3.5
792 |
793 | Various cleanup, typo corrections, etc.
794 |
795 | 2.3.4
796 |
797 | Add a label to 'specs-built' plans for identification/analysis purposes
798 |
799 | 2.3.3
800 |
801 | Retry when receiving HTTP 302 from Bamboo
802 |
803 | 2.3.2
804 |
805 | Add support for triggering plans based on regex, enable/disable shallow cloning
806 |
807 | 2.3.1
808 |
809 | Upgrade to Bamboo 6.7.1
810 |
811 | 2.3.0
812 |
813 | ???
814 |
815 | 2.2.1
816 |
817 | Add support for running in a Docker container
818 |
819 |
820 | 2.2.0
821 |
822 | Add support for ARTIFACTORY upload tasks
823 |
824 | 2.1.5
825 |
826 | Add support for TestNG final tasks
827 |
828 | 2.1.4
829 |
830 | Add support for build status change notifications
831 |
832 | 2.1.3
833 |
834 | Fix a bug in Integer parsing for scheduled tasks
835 |
836 | 2.1.2
837 |
838 | Fix a defect where YAML syntax errors did not result in 'failing' specs files
839 |
840 | 2.1.1
841 |
842 | Fix a defect whereby runs fail if the file already exists for the output
843 |
844 | 2.1.0
845 |
846 | Added support for outputting (in JUnit format) results
847 |
848 | 2.0.1
849 |
850 | Added support for labeling plans
851 |
852 | 2.0.0
853 |
854 | Added support for automatic parsing of plans added to the repository
855 |
856 | 1.1.9
857 |
858 | Add ability to specify the branch to trigger after successful build on.
859 |
860 | 1.1.8
861 |
862 | Added INJECT task type.
863 |
864 | 1.1.7
865 |
866 | Bugfix: honor the branch name in repository settings.
867 | Removed the defaulting of branch cleanup after 30 days.
868 | Added ability to set permissions to all logged in users.
869 |
870 | 1.1.6
871 |
872 | Added support for scheduled triggers.
873 |
874 | 1.1.4
875 |
876 | Added support for arbitrary git repositories in test plans.
877 |
878 | 1.1.1 - 1.1.3
879 |
880 | ???
881 |
882 | 1.1.0
883 |
884 | Clarified docker container configuration by splitting various aspects into sub-groups in the
885 | YAML, allowing clear distinction between the two environmentVariables settings. Also, clean up
886 | the config around detached containers.
887 |
888 | 1.0.0
889 |
890 | Initial release
891 |
--------------------------------------------------------------------------------