├── NOTICE.txt
├── src
├── main
│ ├── resources
│ │ ├── com
│ │ │ └── oracle
│ │ │ │ └── cloud
│ │ │ │ └── baremetal
│ │ │ │ └── jenkins
│ │ │ │ ├── lib
│ │ │ │ ├── taglib
│ │ │ │ ├── idRepeatable_.js
│ │ │ │ ├── idRepeatable.jelly
│ │ │ │ ├── validateButton.jelly
│ │ │ │ └── validateButton_.js
│ │ │ │ ├── BaremetalCloudAgent
│ │ │ │ ├── configure-entries.properties
│ │ │ │ └── configure-entries.jelly
│ │ │ │ ├── BaremetalCloud
│ │ │ │ ├── computerSet.properties
│ │ │ │ ├── help-maxAsyncThreads.html
│ │ │ │ ├── help-instanceCapStr.html
│ │ │ │ ├── config.properties
│ │ │ │ ├── help-credentialsId.html
│ │ │ │ ├── provision.jelly
│ │ │ │ ├── help-templates.html
│ │ │ │ ├── provisionArguments.jelly
│ │ │ │ ├── computerSet.jelly
│ │ │ │ └── config.jelly
│ │ │ │ ├── BaremetalCloudTagsTemplate
│ │ │ │ ├── config.properties
│ │ │ │ └── config.jelly
│ │ │ │ ├── credentials
│ │ │ │ ├── BaremetalCloudCredentialsImpl
│ │ │ │ │ ├── help-tenantId.html
│ │ │ │ │ ├── help-userId.html
│ │ │ │ │ ├── help-regionId.html
│ │ │ │ │ ├── help-apikey.html
│ │ │ │ │ ├── help-fingerprint.html
│ │ │ │ │ ├── help-passphrase.html
│ │ │ │ │ ├── config.properties
│ │ │ │ │ ├── help-instancePrincipals.html
│ │ │ │ │ └── config.jelly
│ │ │ │ └── config.template
│ │ │ │ ├── BaremetalCloudAgentTemplate
│ │ │ │ ├── help-imageCompartmentId.html
│ │ │ │ ├── help-compartmentId.html
│ │ │ │ ├── help-subnetCompartmentId.html
│ │ │ │ ├── help-vcnCompartmentId.html
│ │ │ │ ├── help-subnetId.html
│ │ │ │ ├── help-autoImageUpdate.html
│ │ │ │ ├── help-initScriptTimeoutSeconds.html
│ │ │ │ ├── help-retryTimeoutMins.html
│ │ │ │ ├── help-customJVMOpts.html
│ │ │ │ ├── help-usePublicIP.html
│ │ │ │ ├── help-sshConnectTimeoutSeconds.html
│ │ │ │ ├── help-nsgIds.html
│ │ │ │ ├── help-sshCredentialsId.html
│ │ │ │ ├── help-verifySshKeyPair.html
│ │ │ │ ├── help-shape.html
│ │ │ │ ├── help-verificationStrategy.html
│ │ │ │ ├── help-initScript.html
│ │ │ │ ├── help-instanceNamePrefix.html
│ │ │ │ ├── help-tags.html
│ │ │ │ ├── help-stopOnIdle.html
│ │ │ │ ├── help-jenkinsAgentUser.html
│ │ │ │ ├── help-vcnId.html
│ │ │ │ ├── help-numberOfOcpus.html
│ │ │ │ ├── help-memoryInGBs.html
│ │ │ │ ├── help-startTimeoutSeconds.html
│ │ │ │ ├── help-customJavaPath.html
│ │ │ │ ├── help-exportJenkinsEnvVars.html
│ │ │ │ ├── help-instanceCap.html
│ │ │ │ ├── help-idleTerminationMinutes.html
│ │ │ │ ├── help-assignPublicIP.html
│ │ │ │ ├── help-doNotDisable.html
│ │ │ │ ├── help-availableDomain.html
│ │ │ │ ├── help-imageId.html
│ │ │ │ ├── config.properties
│ │ │ │ └── config.jelly
│ │ │ │ ├── BaremetalCloudNsgTemplate
│ │ │ │ └── config.jelly
│ │ │ │ └── Messages.properties
│ │ └── index.jelly
│ └── java
│ │ └── com
│ │ └── oracle
│ │ └── cloud
│ │ └── baremetal
│ │ └── jenkins
│ │ ├── client
│ │ ├── BaremetalCloudClientFactory.java
│ │ ├── HTTPProxyConfigurator.java
│ │ ├── SDKBaremetalCloudClientFactory.java
│ │ └── BaremetalCloudClient.java
│ │ ├── Clock.java
│ │ ├── credentials
│ │ ├── BaremetalCloudCredentials.java
│ │ ├── BaremetalCloudCredentialsBinding.java
│ │ ├── OciConfigWriter.java
│ │ └── BaremetalCloudCredentialsImpl.java
│ │ ├── retry
│ │ ├── Retry.java
│ │ └── LinearRetry.java
│ │ ├── TimeoutHelper.java
│ │ ├── BaremetalCloudComputer.java
│ │ ├── BaremetalCloudRetentionStrategy.java
│ │ ├── ssh
│ │ ├── FileCreator.java
│ │ └── SshConnector.java
│ │ ├── DynamicResourceBundleHolder.java
│ │ ├── FormValidationValue.java
│ │ ├── BaremetalCloudInstanceMonitor.java
│ │ ├── BaremetalCloudTagsTemplate.java
│ │ ├── JenkinsUtil.java
│ │ ├── SshKeyUtil.java
│ │ ├── BaremetalCloudNsgTemplate.java
│ │ ├── BaremetalCloudTemplateMonitor.java
│ │ └── FormFillFailure.java
└── test
│ └── java
│ └── com
│ └── oracle
│ └── cloud
│ └── baremetal
│ └── jenkins
│ ├── JenkinsUtilUnitTest.java
│ ├── TestClock.java
│ ├── TestRetry.java
│ ├── TestCloud.java
│ ├── BaremetalCloudTestUtils.java
│ ├── BaremetalCloudTest.java
│ ├── BaremetalCloudMockery.java
│ ├── TimeoutHelperTest.java
│ ├── TestCloudAgent.java
│ ├── FormValidationValueUnitTest.java
│ ├── TestMessages.java
│ ├── TestBaremetalCloud.java
│ ├── BaremetalCloudInstanceMonitorUnitTest.java
│ ├── TestBaremetalCloudAgent.java
│ ├── FormFillFailureUnitTest.java
│ ├── BaremetalCloudAgentUnitTest.java
│ └── TestBaremetalCloudAgentTemplate.java
├── .gitignore
├── .travis.yml
├── sbom_generation.yaml
├── SECURITY.md
├── CONTRIBUTING.md
├── CHANGELOG.md
├── pom.xml
└── LICENSE.txt
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018, 2024, Oracle and/or its affiliates.
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/lib/taglib:
--------------------------------------------------------------------------------
1 | Marker file to enable xmlns:x="/com/oracle/cloud/baremetal/jenkins/lib"
2 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgent/configure-entries.properties:
--------------------------------------------------------------------------------
1 | AgentConfiguration=Agent Configuration
2 |
3 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/computerSet.properties:
--------------------------------------------------------------------------------
1 | provision=Provision via Oracle Cloud Infrastructure Compute - {0}
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudTagsTemplate/config.properties:
--------------------------------------------------------------------------------
1 | tagNamespace=Namespace
2 | tagKey=Key
3 | tagValue=Value
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/help-tenantId.html:
--------------------------------------------------------------------------------
1 |
2 | OCID of your tenancy.
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.ipr
3 | *.iws
4 | .idea
5 | target
6 | *.atxa
7 | .classpath
8 | .project
9 | .settings
10 | .DS_Store
11 | /bin
12 | /work
13 | /src_generated
14 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-imageCompartmentId.html:
--------------------------------------------------------------------------------
1 |
2 | The compartment from which to select the image.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-compartmentId.html:
--------------------------------------------------------------------------------
1 |
2 | The compartment from which the new Instance is launched.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-subnetCompartmentId.html:
--------------------------------------------------------------------------------
1 |
2 | The compartment from which to select the Subnet.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/help-userId.html:
--------------------------------------------------------------------------------
1 |
2 | The OCID of the User whose API signing key you are using.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-vcnCompartmentId.html:
--------------------------------------------------------------------------------
1 |
2 | The compartment from which to select the Virtual Cloud Network.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | This plugin allows you to run dynamic slaves in the Oracle Cloud Infrastructure(OCI) Compute environment.
4 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/config.template:
--------------------------------------------------------------------------------
1 | [DEFAULT]
2 | user = %USER%
3 | fingerprint = %FINGERPRINT%
4 | tenancy = %TENANCY%
5 | region = %REGION%
6 | key_file = %KEY_FILE_PATH%
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-subnetId.html:
--------------------------------------------------------------------------------
1 |
2 | Subdivision of your VCN used to separate your network into multiple smaller, distinct networks.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-autoImageUpdate.html:
--------------------------------------------------------------------------------
1 |
2 | Check this Box if you want to use the newest Image if multiple Images exist with same name.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/help-regionId.html:
--------------------------------------------------------------------------------
1 |
2 | The Oracle Cloud Infrastructure region to use for all OCI API requests i.e. "us-phoenix-1".
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-initScriptTimeoutSeconds.html:
--------------------------------------------------------------------------------
1 |
2 | Number of seconds to wait for the completion of Init Script.
3 | Default value is 120 seconds.
4 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-retryTimeoutMins.html:
--------------------------------------------------------------------------------
1 |
2 | If Do Not Disable option is selected, provide number of minutes to wait before retrying provisioning using this template.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-customJVMOpts.html:
--------------------------------------------------------------------------------
1 |
2 | Provide JVM Options string to override the defaults. Eg: To increase heap size, provide this:
3 | -Xms256m -Xmx512m -Djava.awt.headless=true
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-usePublicIP.html:
--------------------------------------------------------------------------------
1 |
2 | By default the Plugin will connect to the public IP of the Agent. If this Option is unchecked, the Plugin will connect to the private IP of the Agent.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/client/BaremetalCloudClientFactory.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.client;
2 |
3 | public interface BaremetalCloudClientFactory {
4 | BaremetalCloudClient createClient(String credentialsId, int maxAsyncThreads);
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-sshConnectTimeoutSeconds.html:
--------------------------------------------------------------------------------
1 |
2 | Number of seconds to wait for new Oracle Cloud Infrastructure compute instance from state "Running" to be SSH connectable from Jenkins controller.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-nsgIds.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-sshCredentialsId.html:
--------------------------------------------------------------------------------
1 |
2 | The Private SSH Key for accessing the OCI instance. For more information, please see
Credentials .
3 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-verifySshKeyPair.html:
--------------------------------------------------------------------------------
1 |
2 | Check whether the SSH public key matches the private key.
3 | Note that you don't have to verify if you are using a custom image and have already stored the public key in the image.
4 |
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk11
4 | dist: trusty
5 | sudo: false
6 | before_install:
7 | - curl -OL https://github.com/oracle/oci-java-sdk/archive/v2.46.0.tar.gz
8 | - tar -xvf v2.46.0.tar.gz
9 | - pushd oci-java-sdk-2.46.0/ && mvn -q clean install && popd
10 | script: mvn clean compile
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/lib/idRepeatable_.js:
--------------------------------------------------------------------------------
1 | Behaviour.specify('INPUT.idRepeatable', 'initialize', 0, function(idInput) {
2 | var nextIdField = idInput.getAttribute('nextIdField');
3 | var nextIdInput = findNearBy(idInput, nextIdField);
4 | idInput.value = nextIdInput.value++;
5 | });
6 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-shape.html:
--------------------------------------------------------------------------------
1 |
2 | In the Compute Service, the shape specifies the number of CPUs and amount of memory allocated to the instance. Oracle Cloud Infrastructure Compute Service offers shapes to fit various computing requirements.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-verificationStrategy.html:
--------------------------------------------------------------------------------
1 |
2 | Check this option to employ knownHost verification strategy. By default,
3 | this strategy adds a new host entry to a file which is used for ssh connection
4 | verification to the hosts.
5 |
6 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-initScript.html:
--------------------------------------------------------------------------------
1 |
2 | Init script specifies commands to be ran after very first slave connection to Jenkins.
3 | Once finished, it created ~/.hudson-run-init file to prevent Jenkins from running
4 | it within each reconnection.
5 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-instanceNamePrefix.html:
--------------------------------------------------------------------------------
1 |
2 | Using this option, you can add additional naming to the Instance in OCI.
3 | Default name is "jenkins-{IP_Address}-{OCID}", using this option it would be "jenkins-{Instance_Name_Prefix}-{IP_Address}-{OCID}"
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-tags.html:
--------------------------------------------------------------------------------
1 |
2 | Tagging allows you to add metadata to resources. See the
3 |
Tagging Overview
4 | documentation for additional information.
5 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-stopOnIdle.html:
--------------------------------------------------------------------------------
1 |
2 | If this is checked, the instance is stopped when the Idle timeout expires.
3 | If the instance is required again, then the plugin will look for a stopped instance that exactly corresponds to the OCI Template specification and resume it if found.
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/help-maxAsyncThreads.html:
--------------------------------------------------------------------------------
1 | The max number of async threads to use to load Templates configuration.
2 | Consider reducing this value for Cloud configurations with a large number of Templates and if some values fail to load.
3 | In this case the logs will show "User-rate limit exceeded"
4 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/help-apikey.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-jenkinsAgentUser.html:
--------------------------------------------------------------------------------
1 |
2 | By default, the plugin starts the Jenkins agent using the default opc user which has sudo privileges. You may specify a different user here to start the Jenkins agent process. This user must be baked into the OS image you select or created through an init script.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-vcnId.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | A Virtual Cloud Network is a virtual version of a traditional network-including subnets, route tables, and gateways-on which your instances run.
4 |
5 | The Drop Down values are in the format - Virtual Cloud Network(Compartment).
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-numberOfOcpus.html:
--------------------------------------------------------------------------------
1 |
2 | You can customize the number of OCPUs that are allocated to a flexible shape. The other resources are scaled proportionately. For more, please see
Compute Shapes .
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/help-fingerprint.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/help-passphrase.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-memoryInGBs.html:
--------------------------------------------------------------------------------
1 |
2 | You can customize amount of memory in GBs that are allocated to a flexible shape.
3 | The other resources are scaled proportionately.
4 | For more, please see
Compute Shapes .
5 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/help-instanceCapStr.html:
--------------------------------------------------------------------------------
1 |
2 | This functionality places a limit on the number of OCI Instances that Jenkins may launch from this Cloud.
3 | For example, if this field is 3, Jenkins will only launch a maximum of 3 OCI Instances for this Cloud and its Template(s).
4 | Leave this field empty to remove the Cloud Instance Cap.
5 |
6 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-startTimeoutSeconds.html:
--------------------------------------------------------------------------------
1 |
2 | Number of seconds to wait for new oracle cloud infrastructure compute instance to reach state "Running".
3 | See the
4 |
Launching Instance
5 | documentation for additional information.
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-customJavaPath.html:
--------------------------------------------------------------------------------
1 |
2 | Provide a custom java path if you wish to not use the java present in the current
3 | user's path to launch agent.jar. Make sure the current user has permission on that
4 | java bin directory. Example: /home/opc/installs/jdk-11.0.9/bin/ with the given user
5 | having permission on this directory.
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-exportJenkinsEnvVars.html:
--------------------------------------------------------------------------------
1 |
2 | Check this Box if you want to use the Jenkins Environment Variables in the Init Script.
3 | This option works only for Linux images.
4 | You can add global Jenkins environment variables in "Manage Jenkins" => "Configure System", under section "Global Properties",
5 | check "Environment variables" checkbox.
6 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/config.properties:
--------------------------------------------------------------------------------
1 | name=Name
2 | credentials=Credentials
3 | testConnection=Test Connection
4 | testConnection.progress=Testing...
5 | instanceCapStr=Instance Cap
6 | maxAsyncThreads=Max number of async threads
7 | templates=Instance Templates
8 | templates.desc=List of templates to use when creating instances to be launched as agents
9 | templates.header=Instance Template
10 | templates.add=Add a new instance template
11 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/Clock.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | public interface Clock {
4 | Clock INSTANCE = new Clock() {
5 | @Override
6 | public long nanoTime() {
7 | return System.nanoTime();
8 | }
9 |
10 | @Override
11 | public void sleep(long millis) throws InterruptedException {
12 | Thread.sleep(millis);
13 | }
14 | };
15 |
16 | long nanoTime();
17 | void sleep(long millis) throws InterruptedException;
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/config.properties:
--------------------------------------------------------------------------------
1 | tenantId=Tenant ID
2 | userId=User ID
3 | fingerprint=Fingerprint
4 | apikey=API Key
5 | passphrase=PassPhrase
6 | regionId=Region
7 | instancePrincipals=Jenkins Controller has Instance Principal Configured
8 | instancePrincipalsTextbox=If above box is checked, only Tenant ID and Region fields are required, while Fingerprint, API Key, Passphrase, User ID fields are ignored.
9 | testConnection=Verify Credentials
10 | testConnection.progress=Connecting...
11 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentials.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.credentials;
2 |
3 | import com.cloudbees.plugins.credentials.common.StandardCredentials;
4 |
5 | public interface BaremetalCloudCredentials extends StandardCredentials {
6 |
7 | String getFingerprint();
8 |
9 | String getApikey();
10 |
11 | String getPassphrase();
12 |
13 | String getTenantId();
14 |
15 | String getUserId();
16 |
17 | String getRegionId();
18 |
19 | boolean isInstancePrincipals();
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgent/configure-entries.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | OCID : ${instance.getInstanceId()}
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudNsgTemplate/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-instanceCap.html:
--------------------------------------------------------------------------------
1 |
2 | This functionality places a limit on the number of OCI Instances that Jenkins may launch from this Template.
3 | For example, if this field is 3, Jenkins will only launch a maximum of 3 OCI Instances for this Template. Template Instance Cap cannot exceed the Cloud Instance Cap.
4 | For example, if Cloud Instance Cap is 2, and the Template Instance Cap is 3, only 2 Instances can be created.
5 | Leave this field empty to remove the Template Instance Cap.
6 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/help-credentialsId.html:
--------------------------------------------------------------------------------
1 |
2 | The Oracle Cloud Infrastructure credentials required to connect to your Oracle Cloud Infrastructure account.
3 | If you want to add an Oracle Cloud Infrastructure credential click "Add" and select "Oracle Cloud Infrastructure Credentials" from the "Kind" Drop-Down.
4 | For more information on OCI credentials and Required Keys, please see
https://docs.cloud.oracle.com/iaas/Content/API/Concepts/apisigningkey.htm.
5 |
6 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/JenkinsUtilUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | import hudson.Util;
7 |
8 | public class JenkinsUtilUnitTest {
9 | @Test
10 | public void testUnescape() {
11 | for (char c1 = 0; c1 < 256; c1++) {
12 | for (char c2 = 0; c2 < 256; c2++) {
13 | String s = new String(new char[] { c1, c2 });
14 | Assert.assertEquals(s, JenkinsUtil.unescape(Util.escape(s)));
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/retry/Retry.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.retry;
2 |
3 | public interface Retry {
4 |
5 | /**
6 | * Check that another retry can be attempted.
7 | *
8 | * @return true, if another retry is possible
9 | */
10 | boolean canRetry();
11 |
12 | /**
13 | * Start attempting to run provided task.
14 | *
15 | * @return task's return value
16 | * @throws Exception on task failure
17 | * TimeoutException on task timeout
18 | */
19 | T run() throws Exception;
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/provision.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ${it.fullDisplayName}
7 |
8 | ${it.getProvisionStartedMessage(request)}
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/TestClock.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.concurrent.TimeUnit;
4 |
5 | public class TestClock implements Clock {
6 | // Initialize to MAX_VALUE to try to detect wraparound bugs: callers should
7 | // use now-begin>=duration rather than now>=begin+duration.
8 | public long nanoTime = Long.MAX_VALUE;
9 |
10 | @Override
11 | public long nanoTime() {
12 | return nanoTime;
13 | }
14 |
15 | @Override
16 | public void sleep(long millis) {
17 | nanoTime += TimeUnit.MILLISECONDS.toNanos(millis);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-idleTerminationMinutes.html:
--------------------------------------------------------------------------------
1 |
2 | Number of minutes for Jenkins to wait before deleting an idle slave, which means complete removal of created oracle compute cloud instance.
3 | A value of 0 (or an empty string) indicates that idle slaves should never be stopped/deleted.
4 | As an example, let's say a slave was started at 11:00 and Idle Termination Minutes was set 5.
5 | The slave executed several Jenkins jobs from 11:00 to 11:20, and has been idle since then.
6 | At 11:25 Jenkins finds the idle timeout of above slave has reached and the slave will be deleted.
7 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudTagsTemplate/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-assignPublicIP.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Each Agent has at least one private IP address and at least one optional public IP address. A private IP address enables the Agent to communicate with other Agents inside the VCN, or with hosts in your on-premises network (via an IPSec VPN or Oracle Cloud Infrastructure FastConnect). A public IP address enables the Agent to communicate with hosts on the internet.
4 | By default, the Plugin will assign a public IP to an Agent, provided the subnet has an available public IP range. If this Option is unchecked, only the private IP is assigned.
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-doNotDisable.html:
--------------------------------------------------------------------------------
1 |
2 | Check this Box if you do not wish to disable the template for any kind of errors encountered while provisioning.
3 | The job tied to this template will remain in the queue until there is a resource available (or) the issue is manually
4 | resolved. The template will be retried after a configured time. Make sure you understand the effects of
5 | this option and configure the retry time accordingly. To ensure some control, the template will be disabled after
6 | encountering 20 continuous failures. You can configure the retry time interval accordingly to achieve your requirement.
7 |
8 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/TestRetry.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.concurrent.Callable;
4 |
5 | import com.oracle.cloud.baremetal.jenkins.retry.Retry;
6 |
7 | /**
8 | * Dummy Retry for testing purposes that just runs the task, but does not retry anything.
9 | */
10 | public class TestRetry implements Retry {
11 | private final Callable task;
12 |
13 | public TestRetry(Callable task) {
14 | this.task = task;
15 | }
16 |
17 | @Override
18 | public boolean canRetry() {
19 | return true;
20 | }
21 |
22 | @Override
23 | public T run() throws Exception {
24 | return task.call();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-availableDomain.html:
--------------------------------------------------------------------------------
1 |
2 | Availability Domains are isolated from each other, fault tolerant, and very unlikely to fail simultaneously or be impacted by the failure of another Availability Domain. When you configure your cloud services, use multiple Availability Domains to ensure high availability and to protect against resource failure. Be aware that some resources must be created within the same Availability Domain, such as an instance and the storage volume attached to it.
3 | See the
4 |
Regions and Availability Domains
5 | documentation for additional information.
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/help-templates.html:
--------------------------------------------------------------------------------
1 |
2 | The templates to use when creating instances in the Oracle Cloud Infrastructure
3 | Compute. When Jenkins requests the Oracle Cloud Infrastructure Compute plugin
4 | to provision additional nodes for a label, the templates are searched for a match
5 | based on the usage and labels configured in the template. The first matching template
6 | is used to create and start instances in Oracle Cloud Infrastructure Compute based
7 | on the template configuration.
8 |
9 | See
10 |
Launching an Instance
11 | documentation for additional information.
12 |
13 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/help-imageId.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | The image is a template of a virtual hard drive that defines the operating system and other software for an instance, for example Oracle Enterprise Linux. When you launch an instance, you can define its characteristics by choosing its image. Oracle provides a set of images you can use. You can also save an image from an instance that you have already configured to use as a template to launch more instances with the same software and customizations
4 | See the
5 | Oracle-Provided Images
6 | documentation for additional information.
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/TestCloud.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.Collection;
4 | import java.util.Collections;
5 |
6 | import hudson.model.Label;
7 | import hudson.slaves.Cloud;
8 | import hudson.slaves.NodeProvisioner.PlannedNode;
9 |
10 | public class TestCloud extends Cloud {
11 | public TestCloud() {
12 | this(TestCloud.class.getName());
13 | }
14 |
15 | public TestCloud(String name) {
16 | super(name);
17 | }
18 |
19 | @Override
20 | public Collection provision(Label label, int excessWorkload) {
21 | return Collections.emptyList();
22 | }
23 |
24 | @Override
25 | public boolean canProvision(Label label) {
26 | return false;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/lib/idRepeatable.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/TimeoutHelper.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | public class TimeoutHelper {
4 | private final Clock clock;
5 | private final long beginNanos;
6 | private final long timeoutNanos;
7 | private final long sleepMillis;
8 |
9 | public TimeoutHelper(Clock clock, long timeoutNanos, long sleepMillis) {
10 | this.clock = clock;
11 | this.beginNanos = clock.nanoTime();
12 | this.timeoutNanos = timeoutNanos;
13 | this.sleepMillis = sleepMillis;
14 | }
15 |
16 | public boolean sleep() throws InterruptedException {
17 | if (timeoutNanos != 0) {
18 | long durationNanos = clock.nanoTime() - beginNanos;
19 | if (durationNanos >= timeoutNanos) {
20 | return false;
21 | }
22 | }
23 |
24 | clock.sleep(sleepMillis);
25 | return true;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudTestUtils.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.Collection;
4 | import java.util.TreeSet;
5 |
6 | import hudson.model.labels.LabelAtom;
7 | import hudson.util.QuotedStringTokenizer;
8 | import jenkins.model.Jenkins;
9 |
10 | public class BaremetalCloudTestUtils {
11 |
12 | /**
13 | * Similar to {@link hudson.model.Label#parse} but does not use
14 | * {@link Jenkins#getInstanceOrNull()}.
15 | */
16 | public static Collection parseLabels(String labels) {
17 | Collection result = new TreeSet<>();
18 | if (labels != null && !labels.isEmpty()) {
19 | for (QuotedStringTokenizer tok = new QuotedStringTokenizer(labels); tok.hasMoreTokens();) {
20 | result.add(new LabelAtom(tok.nextToken()));
21 | }
22 | }
23 | return result;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/lib/validateButton.jelly:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
${attrs.progress}
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudComputer.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import hudson.slaves.AbstractCloudComputer;
4 | import org.kohsuke.stapler.HttpRedirect;
5 | import org.kohsuke.stapler.HttpResponse;
6 | import org.kohsuke.stapler.HttpResponses;
7 |
8 | import java.io.IOException;
9 |
10 | public class BaremetalCloudComputer extends AbstractCloudComputer {
11 | public BaremetalCloudComputer(BaremetalCloudAgent slave) {
12 | super(slave);
13 | }
14 |
15 | @Override
16 | public HttpResponse doDoDelete() throws IOException {
17 | checkPermission(DELETE);
18 | try {
19 | BaremetalCloudAgent node = getNode();
20 | if (node != null) { // No need to terminate nodes again
21 | node.terminate();
22 | }
23 | return new HttpRedirect("../..");
24 | } catch (InterruptedException e) {
25 | return HttpResponses.error(500, e);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/provisionArguments.jelly:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | Enter a number of nodes that should be provisioned
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/sbom_generation.yaml:
--------------------------------------------------------------------------------
1 | version: 0.1
2 | component: build
3 | timeoutInSeconds: 1000
4 | shell: bash
5 | runAs: root
6 | env:
7 | variables:
8 | "JAVA_HOME" : "/usr/lib64/graalvm/graalvm21-ee-java11"
9 | steps:
10 | - type: Command
11 | name: "Installing the Oracle GraalVM 21 for JDK 11"
12 | command: |
13 | yum -y install graalvm21-ee-11-jdk
14 | - type: Command
15 | name: "Setting the JAVA_PATH"
16 | command: |
17 | export PATH=$JAVA_HOME/bin:$PATH
18 | - type: Command
19 | name: "Running Maven cycloneDX plugin command"
20 | command: |
21 | mvn org.cyclonedx:cyclonedx-maven-plugin:2.7.9:makeAggregateBom -DincludeRuntimeScope=true -DincludeCompileScope=true -DincludeProvidedScope=false -DincludeSystemScope=false -DincludeTestScope=false -DoutputFormat=json -DoutputName=artifactSBOM -DschemaVersion=1.4
22 | mv target/artifactSBOM.json ${OCI_PRIMARY_SOURCE_DIR}/artifactSBOM.json
23 | outputArtifacts:
24 | - name: artifactSBOM
25 | type: BINARY
26 | location: ${OCI_PRIMARY_SOURCE_DIR}/artifactSBOM.json
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/help-instancePrincipals.html:
--------------------------------------------------------------------------------
1 |
2 | Checking this box indicates that you have authorized an instance to make API calls in Oracle Cloud Infrastructure services using the Instance Principals functionality.
3 |
4 | After you set up the required resources and policies in OCI, an application running on an instance can call OCI public services, removing the need to configure user credentials or a configuration file.
5 | If you set up this functionality in your Jenkins controller, and check this option, only the
Tenant ID and
Region fields are required, while Fingerprint, API Key, Passphrase, and User ID are ignored.
6 |
7 |
If this box is checked, it is strongly recommended that you click the Verify Credentials button on the lower right of this page.
8 |
9 | See
10 |
Calling Services from an Instance
11 | documentation for additional information.
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudTest.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Ignore;
6 | import org.junit.Rule;
7 | import org.junit.Test;
8 | import org.jvnet.hudson.test.JenkinsRule;
9 |
10 | import hudson.slaves.Cloud;
11 |
12 | public class BaremetalCloudTest {
13 |
14 | @Rule
15 | public JenkinsRule r = new JenkinsRule();
16 |
17 | @Before
18 | public void setUp() throws Exception {
19 |
20 | }
21 |
22 | @After
23 | public void tearDown() throws Exception {
24 |
25 | }
26 |
27 | @Test
28 | @Ignore
29 | public void testConfigRoundtrip() throws Exception {
30 | BaremetalCloud orig = new TestBaremetalCloud.Builder().cloudName("foo").build();
31 | r.jenkins.clouds.add(orig);
32 | r.submit(r.createWebClient().goTo("configure").getFormByName("config"));
33 |
34 | Cloud actual = r.jenkins.clouds.iterator().next();
35 | r.assertEqualBeans(orig, actual, "fingerprint,apikey,passphrase,tenantId,userId");
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudMockery.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.concurrent.atomic.AtomicInteger;
4 |
5 | import org.jmock.api.MockObjectNamingScheme;
6 | import org.jmock.integration.junit4.JUnitRuleMockery;
7 | import org.jmock.lib.CamelCaseNamingScheme;
8 | import org.jmock.lib.concurrent.Synchroniser;
9 |
10 | public class BaremetalCloudMockery extends JUnitRuleMockery {
11 | public BaremetalCloudMockery() {
12 | setNamingScheme(new IdNamingScheme(CamelCaseNamingScheme.INSTANCE));
13 | setThreadingPolicy(new Synchroniser());
14 | }
15 |
16 | private static class IdNamingScheme implements MockObjectNamingScheme {
17 | private final MockObjectNamingScheme scheme;
18 | private final AtomicInteger nextId = new AtomicInteger(0);
19 |
20 | IdNamingScheme(MockObjectNamingScheme scheme) {
21 | this.scheme = scheme;
22 | }
23 |
24 | @Override
25 | public String defaultNameFor(Class> typeToMock) {
26 | return scheme.defaultNameFor(typeToMock) + '.' + nextId.getAndIncrement();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/TimeoutHelperTest.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.concurrent.TimeUnit;
4 |
5 | import org.junit.Assert;
6 | import org.junit.Test;
7 |
8 | public class TimeoutHelperTest {
9 | @Test
10 | public void test() throws Exception {
11 | TestClock clock = new TestClock();
12 | long beginNanos = clock.nanoTime;
13 | TimeoutHelper th = new TimeoutHelper(clock, TimeUnit.MILLISECONDS.toNanos(3), 2);
14 | Assert.assertTrue(th.sleep());
15 | Assert.assertEquals(2, TimeUnit.NANOSECONDS.toMillis(clock.nanoTime - beginNanos));
16 | Assert.assertTrue(th.sleep());
17 | Assert.assertEquals(4, TimeUnit.NANOSECONDS.toMillis(clock.nanoTime - beginNanos));
18 | Assert.assertFalse(th.sleep());
19 | Assert.assertEquals(4, TimeUnit.NANOSECONDS.toMillis(clock.nanoTime - beginNanos));
20 | }
21 |
22 | @Test
23 | public void testNoTimeout() throws Exception {
24 | TimeoutHelper th = new TimeoutHelper(new TestClock(), 0, Long.MAX_VALUE);
25 | Assert.assertTrue(th.sleep());
26 | Assert.assertTrue(th.sleep());
27 | Assert.assertTrue(th.sleep());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/client/HTTPProxyConfigurator.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.client;
2 |
3 | import java.net.HttpURLConnection;
4 | import javax.ws.rs.client.ClientBuilder;
5 |
6 | import org.glassfish.jersey.client.HttpUrlConnectorProvider.ConnectionFactory;
7 | import org.glassfish.jersey.client.ClientConfig;
8 | import org.glassfish.jersey.client.HttpUrlConnectorProvider;
9 |
10 | import com.oracle.bmc.http.DefaultConfigurator;
11 |
12 | import hudson.ProxyConfiguration;
13 |
14 | public class HTTPProxyConfigurator extends DefaultConfigurator {
15 | @Override
16 | public void setConnectorProvider(ClientBuilder builder) {
17 | ClientConfig clientConfig = new ClientConfig();
18 |
19 | // OCI API HTTP proxy workaround
20 | ConnectionFactory connectionFactory = url -> (HttpURLConnection) ProxyConfiguration.open(url);
21 |
22 | // 1) enable workaround for 'patch' requests
23 | HttpUrlConnectorProvider provider = new HttpUrlConnectorProvider()
24 | .useSetMethodWorkaround()
25 | .connectionFactory(connectionFactory);
26 |
27 | clientConfig.connectorProvider(provider);
28 | builder.withConfig(clientConfig);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudRetentionStrategy.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.logging.Logger;
4 |
5 | import javax.annotation.concurrent.GuardedBy;
6 |
7 | import hudson.slaves.AbstractCloudComputer;
8 | import hudson.slaves.CloudRetentionStrategy;
9 | import hudson.slaves.OfflineCause.SimpleOfflineCause;
10 |
11 | public class BaremetalCloudRetentionStrategy extends CloudRetentionStrategy {
12 | private static final Logger LOGGER = Logger.getLogger(BaremetalCloud.class.getName());
13 |
14 | public BaremetalCloudRetentionStrategy(int idleMinutes) {
15 | super(idleMinutes);
16 | }
17 |
18 | /**
19 | * Prevent {@link CloudRetentionStrategy} from terminating the Computer if it is
20 | * in offline state set by user (e.g. from Web UI) or by another plugin.
21 | */
22 | @Override
23 | @GuardedBy("hudson.model.Queue.lock")
24 | public long check(final AbstractCloudComputer c) {
25 | if (c.isOffline() && c.getOfflineCause() instanceof SimpleOfflineCause) {
26 | LOGGER.fine(c.getDisplayName() + ": Node is set temporarily offline - will not terminate");
27 | return 1;
28 | }
29 |
30 | return super.check(c);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/computerSet.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ${t.displayName}
15 |
16 |
17 |
18 |
19 |
20 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/ssh/FileCreator.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.ssh;
2 |
3 | import hudson.FilePath;
4 | import hudson.remoting.VirtualChannel;
5 | import jenkins.MasterToSlaveFileCallable;
6 |
7 | import java.io.File;
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 | import java.io.OutputStream;
11 | import java.nio.charset.StandardCharsets;
12 |
13 | public class FileCreator {
14 |
15 | String createfilename;
16 |
17 | public void createFileOnMaster() throws IOException, InterruptedException {
18 | FilePath workspace = new FilePath(new File("."));
19 | FileCreationCallable fcc = new FileCreationCallable();
20 | fcc.filename = createfilename;
21 | workspace.act(fcc);
22 | }
23 |
24 | private static class FileCreationCallable extends MasterToSlaveFileCallable {
25 | private String filename;
26 |
27 | @Override
28 | public Void invoke(File file, VirtualChannel virtualChannel) throws IOException {
29 | File newFile = new File(file, filename);
30 | try (OutputStream outputStream = new FileOutputStream(newFile)) {
31 | String content = "";
32 | outputStream.write(content.getBytes(StandardCharsets.UTF_8));
33 | }
34 | return null;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/lib/validateButton_.js:
--------------------------------------------------------------------------------
1 | // Alternative to validateBehavior from hudson-behavior.js that uses findNearBy
2 | // rather than findPreviousFormItem.
3 | function validateButtonUsingFindNearBy(checkUrl, paramList, button) {
4 | button = button._button;
5 |
6 | var parameters = {};
7 | paramList.split(',').forEach(function(name) {
8 | // Use findNearBy rather than findPreviousFormItem.
9 | var p = findNearBy(button, name);
10 | if (p) {
11 | // Removing leading '../', etc.
12 | name = name.substring(name.lastIndexOf('/') + 1);
13 | parameters[name] = controlValue(p);
14 | }
15 | });
16 |
17 | var spinner = button.closest('DIV').nextElementSibling;
18 | var target = spinner.nextElementSibling;
19 | spinner.style.display = 'block';
20 | fetch(checkUrl, {
21 | method: "post",
22 | body: new URLSearchParams(parameters),
23 | headers: crumb.wrap(),
24 | }).then((rsp) => {
25 | console.log("RSP"+rsp);
26 | rsp.text().then((responseText) => {
27 | spinner.style.display = "none";
28 | applyErrorMessage(target, rsp);
29 | layoutUpdateCallback.call();
30 | var s = rsp.headers.get("script");
31 | try {
32 | geval(s);
33 | } catch (e) {
34 | window.alert("failed to evaluate " + s + "\n" + e.message);
35 | }
36 | });
37 | });
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloud/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/TestCloudAgent.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.io.IOException;
4 |
5 | import hudson.model.TaskListener;
6 | import hudson.model.Descriptor.FormException;
7 | import hudson.slaves.AbstractCloudComputer;
8 | import hudson.slaves.AbstractCloudSlave;
9 | import jenkins.model.Jenkins;
10 |
11 | @SuppressWarnings("serial")
12 | public class TestCloudAgent extends AbstractCloudSlave {
13 | public static class Builder {
14 | public TestCloudAgent build() {
15 | return (TestCloudAgent)Jenkins.XSTREAM2.fromXML(" \n");
16 | }
17 | }
18 |
19 | protected TestCloudAgent() throws FormException, IOException {
20 | // Always create an instance by deserializing XML to avoid the Slave
21 | // constructor, which attempts to initialize nodeProperties using
22 | // Jenkins.getInstance(), which is null.
23 | super(null, null, null, null, null, null, null, null, null);
24 | throw new UnsupportedOperationException();
25 | }
26 |
27 | @Override
28 | protected Object readResolve() {
29 | // Override to avoid Slave.readResolve, which attempts to initialize
30 | // nodeProperties using Jenkins.getInstance(), which is null.
31 | return this;
32 | }
33 |
34 | @Override
35 | public AbstractCloudComputer> createComputer() {
36 | throw new UnsupportedOperationException();
37 | }
38 |
39 | @Override
40 | protected void _terminate(TaskListener listener) throws IOException, InterruptedException {
41 | throw new UnsupportedOperationException();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting security vulnerabilities
2 |
3 | Oracle values the independent security research community and believes that
4 | responsible disclosure of security vulnerabilities helps us ensure the security
5 | and privacy of all our users.
6 |
7 | Please do NOT raise a GitHub Issue to report a security vulnerability. If you
8 | believe you have found a security vulnerability, please submit a report to
9 | [secalert_us@oracle.com][1] preferably with a proof of concept. Please review
10 | some additional information on [how to report security vulnerabilities to Oracle][2].
11 | We encourage people who contact Oracle Security to use email encryption using
12 | [our encryption key][3].
13 |
14 | We ask that you do not use other channels or contact the project maintainers
15 | directly.
16 |
17 | Non-vulnerability related security issues including ideas for new or improved
18 | security features are welcome on GitHub Issues.
19 |
20 | ## Security updates, alerts and bulletins
21 |
22 | Security updates will be released on a regular cadence. Many of our projects
23 | will typically release security fixes in conjunction with the
24 | Oracle Critical Patch Update program. Additional
25 | information, including past advisories, is available on our [security alerts][4]
26 | page.
27 |
28 | ## Security-related information
29 |
30 | We will provide security related information such as a threat model, considerations
31 | for secure use, or any known security issues in our documentation. Please note
32 | that labs and sample code are intended to demonstrate a concept and may not be
33 | sufficiently hardened for production use.
34 |
35 | [1]: mailto:secalert_us@oracle.com
36 | [2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html
37 | [3]: https://www.oracle.com/security-alerts/encryptionkey.html
38 | [4]: https://www.oracle.com/security-alerts/
39 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/config.properties:
--------------------------------------------------------------------------------
1 | description=Description
2 | compartmentId=Compartment
3 | availableDomain=Availability Domain
4 | imageCompartmentId=Image Compartment
5 | imageId=Image
6 | shape=Shape
7 | vcnCompartmentId=Virtual Cloud Network Compartment
8 | vcnId=Virtual Cloud Network
9 | subnetCompartmentId=Subnet Compartment
10 | subnetId=Subnet
11 | sshPublickey=SSH Public Key
12 | sshPrivatekey=SSH Private Key
13 | sshCredentialsId=SSH credentials
14 | verifySshKeyPair=Verify SSH Key Pair
15 | verifySshKeyPair.progress=Verifying...
16 | labelString=Labels
17 | startTimeoutSeconds=Instance Creation Timeout
18 | sshConnectTimeoutSeconds=Instance SSH Connection Timeout
19 | idleTerminationMinutes=Idle Termination Minutes
20 | jenkinsAgentUser=Jenkins Agent User
21 | customJavaPath=Custom Java Path
22 | customJVMOpts=Override JVM Options
23 | initScript=Init Script
24 | numExecutors=Number Of Executors
25 | remoteFS=Remote FS Root
26 | sshUser=Remote SSH User
27 | autoImageUpdate=Identical Named Images
28 | nsgIds=Network Security Groups
29 | assignPublicIP=Assign Public IP Address
30 | usePublicIP=Connect Agent using Public IP
31 | stopOnIdle=Stop on Idle Timeout
32 | initScriptTimeoutSeconds=Init Script Timeout
33 | instanceCap=Template Instance Cap
34 | numberOfOcpus=Number of OCPUs
35 | disableCause=This template is temporarily disabled due to consecutive provision failures, the most recent error is : {0}
36 | reenable=To re-enable it, check the Jenkins log and fix the configuration accordingly, and then save and reload the page.
37 | tags=Tags
38 | instanceNamePrefix=Instance Name Prefix
39 | memoryInGBs=Memory in GBs
40 | exportJenkinsEnvVars=Export Jenkins Env Variables to Init Script
41 | doNotDisable=Do Not Disable Template
42 | retryTimeoutMinutes= Template retry Timeout
43 | sshVerificationStrategy= Employ knownHost verification.
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/DynamicResourceBundleHolder.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.text.MessageFormat;
4 | import java.util.Locale;
5 | import java.util.ResourceBundle;
6 |
7 | import org.jvnet.localizer.LocaleProvider;
8 | import org.jvnet.localizer.ResourceBundleHolder;
9 |
10 | /**
11 | * An alternative to {@link ResourceBundleHolder} that does not require a
12 | * statically generated class with the same name as the properties file.
13 | */
14 | public class DynamicResourceBundleHolder {
15 | /**
16 | * Returns a {@link ResourceBundleHolder} for the given class.
17 | *
18 | * @param owner the resource bundle owner
19 | * @param shortName the resource bundle name relative to the owner
20 |
21 | * @return a {@link ResourceBundleHolder} for the given class
22 | */
23 | public synchronized static DynamicResourceBundleHolder get(Class> owner, String shortName) {
24 | String name = owner.getName().replace('.', '/') + '/' + shortName;
25 | return new DynamicResourceBundleHolder(owner.getClassLoader(), name);
26 | }
27 |
28 | ClassLoader classLoader;
29 | private final String name;
30 |
31 | private DynamicResourceBundleHolder(ClassLoader classLoader, String name) {
32 | this.classLoader = classLoader;
33 | this.name = name;
34 | }
35 |
36 | private synchronized ResourceBundle get(Locale locale) {
37 | return ResourceBundle.getBundle(name, locale, classLoader);
38 | }
39 |
40 | /**
41 | * Formats a resource specified by the given key by using the default locale
42 | * @param key key
43 | * @param args object(s) to format
44 | * @return the formatted string
45 | */
46 | public String format(String key, Object... args) {
47 | return MessageFormat.format(get(LocaleProvider.getLocale()).getString(key),args);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/Messages.properties:
--------------------------------------------------------------------------------
1 | FormValidation.ValidateRequired=Field is required
2 |
3 | BaremetalCloud.cloudName.duplicate=Oracle Cloud Infrastructure Compute named \u2018{0}\u2019 already exists
4 |
5 | BaremetalCloud.testConnection.success=Successfully connected to Oracle Cloud Infrastructure Compute
6 | BaremetalCloud.testConnection.error=Failed to connect to Oracle Cloud Infrastructure Compute: {0}
7 | BaremetalCloud.testConnection.unauthorized=Invalid credentials
8 |
9 | BaremetalCloudAgentTemplate.labelString.exclusiveEmpty=You may want to assign labels to this node; it's marked to only run jobs that are exclusively tied to itself or a label.
10 |
11 | BaremetalCloud.provision.templateNotFound=Unable to find agent template
12 | BaremetalCloud.provision.templateDisabled=Agent template has been disabled
13 | BaremetalCloud.provision.started=Started provisioning node(s) {0} with {1} executors.
14 |
15 | BaremetalCloud.termination.offlineCause=This Agent is being terminated
16 |
17 | BaremetalCloudAgentTemplate.privateKey.unable=Unable to read SSH Private Key: {0}
18 | BaremetalCloudAgentTemplate.privateKey.invalid=Invalid SSH Private Key
19 |
20 | BaremetalCloudAgentTemplate.verifySshKeyPair.unable=Unable to verify the SSH key pair: {0}
21 | BaremetalCloudAgentTemplate.verifySshKeyPair.success=Successfully matched the SSH public and private key pair
22 | BaremetalCloudAgentTemplate.verifySshKeyPair.publicKeyEmpty=The SSH public key is empty
23 | BaremetalCloudAgentTemplate.verifySshKeyPair.mismatch=The SSH Private Key does not match the public key
24 |
25 | BaremetalCloudAgentTemplate.usePublicIP.unable=Public IP is not assigned.
26 | BaremetalCloudAgentTemplate.assignPublicIP.unable=The selected subnet is private, can not assign Public IP.
27 | BaremetalCloudAgentTemplate.ocpu.flex.empty=Please select appropriate value for selected shape
28 | BaremetalCloudAgentTemplate.ocpu.nonflex.nonempty=Please select appropriate shape that supports this option
29 | BaremetalCloudAgentTemplate_prefix_contains_spaces=Instance Name Prefix can not contain whitespaces
30 | FormFillFailure.error=ERROR: {0}
31 | BaremetalCloudAgentTemplate.doNotDisable.warningmsg=There can be performance issues, make sure you understand the effects.
32 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to this repository
2 |
3 | We welcome your contributions! There are multiple ways to contribute.
4 |
5 | ## Opening issues
6 |
7 | For bugs or enhancement requests, please file a GitHub issue unless it's
8 | security related. When filing a bug remember that the better written the bug is,
9 | the more likely it is to be fixed. If you think you've found a security
10 | vulnerability, do not raise a GitHub issue and follow the instructions in our
11 | [security policy](./SECURITY.md).
12 |
13 | ## Contributing code
14 |
15 | We welcome your code contributions. Before submitting code via a pull request,
16 | you will need to have signed the [Oracle Contributor Agreement][OCA] (OCA) and
17 | your commits need to include the following line using the name and e-mail
18 | address you used to sign the OCA:
19 |
20 | ```text
21 | Signed-off-by: Your Name
22 | ```
23 |
24 | This can be automatically added to pull requests by committing with `--sign-off`
25 | or `-s`, e.g.
26 |
27 | ```text
28 | git commit --signoff
29 | ```
30 |
31 | Only pull requests from committers that can be verified as having signed the OCA
32 | can be accepted.
33 |
34 | ## Pull request process
35 |
36 | 1. Ensure there is an issue created to track and discuss the fix or enhancement
37 | you intend to submit.
38 | 1. Fork this repository.
39 | 1. Create a branch in your fork to implement the changes. We recommend using
40 | the issue number as part of your branch name, e.g. `1234-fixes`.
41 | 1. Ensure that any documentation is updated with the changes that are required
42 | by your change.
43 | 1. Ensure that any samples are updated if the base image has been changed.
44 | 1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly
45 | what your changes are meant to do and provide simple steps on how to validate.
46 | your changes. Ensure that you reference the issue you created as well.
47 | 1. We will assign the pull request to 2-3 people for review before it is merged.
48 |
49 | ## Code of conduct
50 |
51 | Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If you'd
52 | like more specific guidelines, see the [Contributor Covenant Code of Conduct][COC].
53 |
54 | [OCA]: https://oca.opensource.oracle.com
55 | [COC]: https://www.contributor-covenant.org/version/1/4/code-of-conduct/
56 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/FormValidationValueUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | import hudson.util.FormValidation;
7 |
8 | public class FormValidationValueUnitTest {
9 | @Test
10 | public void testOk() {
11 | FormValidationValue fvv = FormValidationValue.ok("value");
12 | Assert.assertNotNull(fvv.toString());
13 | Assert.assertEquals(FormValidation.Kind.OK, fvv.getFormValidation().kind);
14 | Assert.assertTrue(fvv.isOk());
15 | Assert.assertEquals("value", fvv.getValue());
16 | }
17 |
18 | @Test
19 | public void testError() {
20 | FormValidationValue fvv = FormValidationValue.error("error");
21 | Assert.assertNotNull(fvv.toString());
22 | Assert.assertEquals(FormValidation.Kind.ERROR, fvv.getFormValidation().kind);
23 | Assert.assertFalse(fvv.isOk());
24 | Assert.assertEquals("error", fvv.getFormValidation().getMessage());
25 | }
26 |
27 | @Test(expected = IllegalArgumentException.class)
28 | public void testErrorOk() {
29 | FormValidationValue.error(FormValidation.ok());
30 | }
31 |
32 | @Test
33 | public void testErrorDefaultValue() {
34 | Assert.assertNull(FormValidationValue.error("error").getValue());
35 | Assert.assertEquals(1, (int)FormValidationValue.error("error", 1).getValue());
36 | }
37 |
38 | @Test
39 | public void testValidatePositiveInteger() {
40 | FormValidationValue fv = FormValidationValue.validatePositiveInteger("2", 0);
41 | Assert.assertTrue(fv.isOk());
42 | Assert.assertEquals(2, (int)fv.getValue());
43 |
44 | fv = FormValidationValue.validatePositiveInteger("0", 2);
45 | Assert.assertFalse(fv.isOk());
46 | Assert.assertEquals(2, (int)fv.getValue());
47 | }
48 |
49 | @Test
50 | public void testValidateNonNegativeInteger() {
51 | FormValidationValue fv = FormValidationValue.validateNonNegativeInteger("0", 2);
52 | Assert.assertTrue(fv.isOk());
53 | Assert.assertEquals(0, (int)fv.getValue());
54 |
55 | fv = FormValidationValue.validatePositiveInteger("-1", 2);
56 | Assert.assertFalse(fv.isOk());
57 | Assert.assertEquals(2, (int)fv.getValue());
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/FormValidationValue.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import hudson.util.FormValidation;
4 |
5 | public class FormValidationValue {
6 | public static FormValidationValue ok(T value) {
7 | return new FormValidationValue<>(FormValidation.ok(), value);
8 | }
9 |
10 | public static FormValidationValue error(String s) {
11 | return error(s, null);
12 | }
13 |
14 | public static FormValidationValue error(String s, T defaultValue) {
15 | return error(FormValidation.error(s), defaultValue);
16 | }
17 |
18 | public static FormValidationValue error(FormValidation formValidation) {
19 | return error(formValidation, null);
20 | }
21 |
22 | public static FormValidationValue error(FormValidation formValidation, T defaultValue) {
23 | if (formValidation.kind == FormValidation.Kind.OK) {
24 | throw new IllegalArgumentException();
25 | }
26 | return new FormValidationValue<>(formValidation, defaultValue);
27 | }
28 |
29 | public static FormValidationValue validatePositiveInteger(String value, int defaultValue) {
30 | FormValidation fv = FormValidation.validatePositiveInteger(value);
31 | if (fv.kind != FormValidation.Kind.OK) {
32 | return error(fv, defaultValue);
33 | }
34 | return ok(Integer.parseInt(value));
35 | }
36 |
37 | public static FormValidationValue validateNonNegativeInteger(String value, int defaultValue) {
38 | FormValidation fv = FormValidation.validateNonNegativeInteger(value);
39 | if (fv.kind != FormValidation.Kind.OK) {
40 | return error(fv, defaultValue);
41 | }
42 | return ok(Integer.parseInt(value));
43 | }
44 |
45 | private final FormValidation formValidation;
46 | private final T value;
47 |
48 | private FormValidationValue(FormValidation formValidation, T value) {
49 | this.formValidation = formValidation;
50 | this.value = value;
51 | }
52 |
53 | @Override
54 | public String toString() {
55 | return isOk() ? "OK: " + value : formValidation.toString();
56 | }
57 |
58 | public FormValidation getFormValidation() {
59 | return formValidation;
60 | }
61 |
62 | public boolean isOk() {
63 | return formValidation.kind == FormValidation.Kind.OK;
64 | }
65 |
66 | public T getValue() {
67 | return value;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/client/SDKBaremetalCloudClientFactory.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.client;
2 |
3 | import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider;
4 | import java.io.ByteArrayInputStream;
5 | import java.nio.charset.StandardCharsets;
6 |
7 | import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider;
8 | import com.oracle.cloud.baremetal.jenkins.BaremetalCloud;
9 | import com.oracle.cloud.baremetal.jenkins.credentials.BaremetalCloudCredentials;
10 | import java.util.logging.Level;
11 | import java.util.logging.Logger;
12 |
13 | public class SDKBaremetalCloudClientFactory implements BaremetalCloudClientFactory {
14 | public static final BaremetalCloudClientFactory INSTANCE = new SDKBaremetalCloudClientFactory();
15 | private static final Logger LOGGER = Logger.getLogger(SDKBaremetalCloudClient.class.getName());
16 |
17 | private SDKBaremetalCloudClientFactory() {}
18 |
19 | @Override
20 | public BaremetalCloudClient createClient (String credentialsId, int maxAsyncThreads) {
21 | BaremetalCloudCredentials credentials = (BaremetalCloudCredentials) BaremetalCloud.matchCredentials(BaremetalCloudCredentials.class,credentialsId);
22 | if (credentials != null) {
23 | if (!credentials.isInstancePrincipals()) {
24 | SimpleAuthenticationDetailsProvider provider = SimpleAuthenticationDetailsProvider.builder()
25 | .fingerprint(credentials.getFingerprint())
26 | .passPhrase(credentials.getPassphrase())
27 | .privateKeySupplier(() -> new ByteArrayInputStream(credentials.getApikey().getBytes(StandardCharsets.UTF_8)))
28 | .tenantId(credentials.getTenantId())
29 | .userId(credentials.getUserId())
30 | .build();
31 | return new SDKBaremetalCloudClient(provider, credentials.getRegionId(), maxAsyncThreads);
32 | } else {
33 | try {
34 | InstancePrincipalsAuthenticationDetailsProvider provider = InstancePrincipalsAuthenticationDetailsProvider.builder().build();
35 | return new SDKBaremetalCloudClient(provider, credentials.getRegionId(), maxAsyncThreads, credentials.getTenantId());
36 | } catch (Exception e){
37 | LOGGER.log(Level.INFO,"Failed to use Calling Services from an Instance");
38 | }
39 | }
40 | }
41 | LOGGER.log(Level.INFO,"Failed to create client!");
42 | return null;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/TestMessages.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.io.File;
4 | import java.io.InputStream;
5 | import java.lang.reflect.Field;
6 | import java.net.MalformedURLException;
7 | import java.net.URL;
8 |
9 | import org.apache.commons.io.IOUtils;
10 | import org.jvnet.localizer.ResourceBundleHolder;
11 |
12 | /**
13 | * Allow the {@link Messages} class to be used even if src/main/resources is not
14 | * present on the Eclipse project Build Path.
15 | */
16 | public class TestMessages {
17 | private static final ResourceBundleHolder holder;
18 |
19 | private static class ResourceClassLoader extends ClassLoader {
20 | private final File resourcesDir = new File("src/main/resources");
21 |
22 | @Override
23 | public URL getResource(String name) {
24 | File file = new File(resourcesDir, name);
25 | if (file.isFile()) {
26 | try {
27 | return file.toURI().toURL();
28 | } catch (MalformedURLException e) {
29 | throw new Error(e);
30 | }
31 | }
32 | return super.getResource(name);
33 | }
34 |
35 | Class> defineClass(Class> c) throws Exception {
36 | // Redefine the class in this class loader so that when
37 | // ResourceBundleHolder calls Class.getResource, it will
38 | // ultimately call our overridden getResource.
39 | String name = c.getName();
40 | try (InputStream in = c.getResourceAsStream(name.substring(name.lastIndexOf('.') + 1).replace('.', '/') + ".class")) {
41 | byte[] buf = IOUtils.toByteArray(in);
42 | return defineClass(name, buf, 0, buf.length);
43 | }
44 | }
45 | }
46 |
47 | static {
48 | final Class> messagesClass = Messages.class;
49 | holder = ResourceBundleHolder.get(messagesClass);
50 |
51 | try {
52 | ResourceClassLoader resourceClassLoader = new ResourceClassLoader();
53 |
54 | Field resourceBundleHolderOwnerField = ResourceBundleHolder.class.getField("owner");
55 | resourceBundleHolderOwnerField.setAccessible(true);
56 | resourceBundleHolderOwnerField.set(holder, resourceClassLoader.defineClass(messagesClass));
57 |
58 | // BaremetalCloudAgentTemplate.ConfigMessages.holder.classLoader = resourceClassLoader;
59 | } catch (Exception e) {
60 | throw new Error(e);
61 | }
62 | }
63 |
64 | public static void init() {}
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudInstanceMonitor.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.io.IOException;
4 | import java.util.*;
5 | import java.util.concurrent.TimeUnit;
6 | import java.util.logging.Level;
7 | import java.util.logging.Logger;
8 |
9 | import hudson.Extension;
10 | import hudson.model.AsyncPeriodicWork;
11 | import hudson.model.Node;
12 | import hudson.model.TaskListener;
13 | import hudson.slaves.Cloud;
14 |
15 | @Extension
16 | public class BaremetalCloudInstanceMonitor extends AsyncPeriodicWork {
17 | private static final Logger LOGGER = Logger.getLogger(BaremetalCloudInstanceMonitor.class.getName());
18 |
19 | private static final Long recurrencePeriod = TimeUnit.MINUTES.toMillis(10);
20 |
21 | public BaremetalCloudInstanceMonitor(){
22 | super("Oracle Oracle Cloud Infrastructure Compute instances monitor");
23 | LOGGER.log(Level.FINE, "Oracle Cloud Infrastructure Compute check alive period is {0}ms", recurrencePeriod);
24 | }
25 |
26 | @Override
27 | public long getRecurrencePeriod() {
28 | return recurrencePeriod;
29 | }
30 |
31 | List getNodes() {
32 | return JenkinsUtil.getJenkinsInstance().getNodes();
33 | }
34 |
35 | List getClouds() { return JenkinsUtil.getJenkinsInstance().clouds.toList(); }
36 |
37 | @Override
38 | protected void execute(TaskListener listener) {
39 |
40 | for(Node node : getNodes()){
41 | if(node instanceof BaremetalCloudAgent){
42 | final BaremetalCloudAgent agent = (BaremetalCloudAgent)node;
43 | try{
44 | if(! agent.isAlive()){
45 | LOGGER.info("Cloud Infrastructure instance is offline: " + agent.getDisplayName());
46 | agent._terminate(listener);
47 | LOGGER.info("Cloud Infrastructure instance is terminated: " + agent.getDisplayName());
48 | removeNode(agent);
49 | }else{
50 | LOGGER.info("Cloud Infrastructure instance is online: " + agent.getDisplayName());
51 | }
52 | } catch (IOException | InterruptedException | RuntimeException e){
53 | LOGGER.info("Failed to terminate node : " + agent.getDisplayName());
54 | LOGGER.info("ERROR : " + e.getMessage());
55 | }
56 | }
57 | }
58 |
59 | LOGGER.log(Level.FINE,"Monitoring the compute plugin online instances");
60 | for (Cloud c : getClouds()) {
61 | if (c instanceof BaremetalCloud) {
62 | BaremetalCloud cloud = (BaremetalCloud) c;
63 | for (BaremetalCloudAgentTemplate template: cloud.getTemplates()) {
64 | cloud.getTemplateNodeCount(template.getTemplateId());
65 | }
66 | }
67 | }
68 | }
69 |
70 | void removeNode(BaremetalCloudAgent agent){
71 | try{
72 | JenkinsUtil.getJenkinsInstance().removeNode(agent);
73 | } catch (IOException e) {
74 | LOGGER.log(Level.WARNING, "Failed to remove node: " + agent.getDisplayName());
75 | }
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/TestBaremetalCloud.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.List;
4 | import java.util.Objects;
5 | import java.util.concurrent.Callable;
6 |
7 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClient;
8 | import com.oracle.cloud.baremetal.jenkins.retry.Retry;
9 |
10 | public class TestBaremetalCloud extends BaremetalCloud{
11 | public static class Builder {
12 | String cloudName = "cloudName";
13 | String credentialsId;
14 | String instanceCapStr;
15 | String maxAsyncThreads;
16 | int nextTemplateId;
17 | List extends BaremetalCloudAgentTemplate> templates;
18 | BaremetalCloudClient client;
19 | Clock clock;
20 |
21 | public Builder cloudName(String cloudName) {
22 | this.cloudName = cloudName;
23 | return this;
24 | }
25 |
26 | public Builder credentialsId(String CredentialsId) {
27 | this.credentialsId = credentialsId;
28 | return this;
29 | }
30 |
31 | public Builder instanceCapStr(String instanceCapStr) {
32 | this.instanceCapStr = instanceCapStr;
33 | return this;
34 | }
35 | public Builder maxAsyncThreads(String maxAsyncThreads) {
36 | this.maxAsyncThreads = maxAsyncThreads;
37 | return this;
38 | }
39 | public Builder nextTemplateId(int nextTemplateId) {
40 | this.nextTemplateId = nextTemplateId;
41 | return this;
42 | }
43 |
44 | public Builder templates(List extends BaremetalCloudAgentTemplate> templates) {
45 | this.templates = templates;
46 | return this;
47 | }
48 |
49 | public Builder client(BaremetalCloudClient client) {
50 | this.client = client;
51 | return this;
52 | }
53 |
54 | public Builder clock(Clock clock) {
55 | this.clock = clock;
56 | return this;
57 | }
58 |
59 | public TestBaremetalCloud build(){
60 | return new TestBaremetalCloud(this);
61 | }
62 | }
63 |
64 | private final BaremetalCloudClient client;
65 |
66 | public TestBaremetalCloud(){
67 | this(new Builder());
68 | }
69 |
70 | public TestBaremetalCloud(Builder builder){
71 | super(
72 | builder.cloudName,
73 | builder.credentialsId,
74 | builder.instanceCapStr,
75 | builder.maxAsyncThreads,
76 | builder.nextTemplateId,
77 | builder.templates);
78 | this.client = builder.client;
79 | }
80 |
81 | @Override
82 | public BaremetalCloudClient getClient() {
83 | return Objects.requireNonNull(client, "client");
84 | }
85 |
86 | @Override
87 | public Retry getTerminationRetry(Callable task) {
88 | return new TestRetry(task);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/retry/LinearRetry.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.retry;
2 |
3 | import java.time.Duration;
4 | import java.util.concurrent.Callable;
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 | import java.util.concurrent.Future;
8 | import java.util.concurrent.TimeUnit;
9 | import java.util.logging.Logger;
10 |
11 | public class LinearRetry implements Retry {
12 | private static final Logger LOGGER = Logger.getLogger(LinearRetry.class.getName());
13 |
14 | private static final int DEFAULT_MAX_RETRIES = 3;
15 | private static final Duration DEFAULT_RETRY_DELAY = Duration.ofSeconds(30);
16 | private static final Duration DEFAULT_RETRY_TIMEOUT = Duration.ofSeconds(30);
17 |
18 | private final Callable task;
19 | private final int maxRetries;
20 | private final Duration retryDelay;
21 | private final Duration retryTimeout;
22 |
23 | private int retries;
24 |
25 | public LinearRetry(Callable task) {
26 | this(task, DEFAULT_MAX_RETRIES);
27 | }
28 |
29 | public LinearRetry(Callable task, int maxRetries) {
30 | this(task, maxRetries, DEFAULT_RETRY_DELAY, DEFAULT_RETRY_TIMEOUT);
31 | }
32 |
33 | /**
34 | * @param task Callable that throws exception on error
35 | * @param maxRetries Positive number of attempts to call task before raising exception
36 | * @param retryDelay Time between attempts
37 | * @param retryTimeout Maximum task execution time after which it is considered failed
38 | */
39 | public LinearRetry(Callable task, int maxRetries, Duration retryDelay, Duration retryTimeout) {
40 | this.task = task;
41 | this.maxRetries = maxRetries;
42 | this.retryDelay = retryDelay;
43 | this.retryTimeout = retryTimeout;
44 | this.retries = 0;
45 | }
46 |
47 | /**
48 | * @see com.oracle.cloud.baremetal.jenkins.retry.Retry#canRetry()
49 | */
50 | @Override
51 | public boolean canRetry() {
52 | return retries < maxRetries;
53 | }
54 |
55 | /**
56 | * @see com.oracle.cloud.baremetal.jenkins.retry.Retry#run()
57 | */
58 | @Override
59 | public T run() throws Exception {
60 | ExecutorService executor = Executors.newSingleThreadExecutor();
61 |
62 | LOGGER.fine("Start retring for task: " + task.toString());
63 |
64 | while(canRetry()) {
65 | ++retries;
66 | LOGGER.fine("Retry attempt " + retries + " of " + maxRetries + " for task: " + task.toString());
67 |
68 | Future handler = executor.submit(task);
69 |
70 | try {
71 | return handler.get(retryTimeout.toMillis(), TimeUnit.MILLISECONDS);
72 | } catch (Exception e) {
73 | handler.cancel(true);
74 |
75 | if (!canRetry()) {
76 | LOGGER.info("All retry attempts for task: " + task.toString() + " failed");
77 | throw e;
78 | }
79 | }
80 |
81 | Thread.sleep(retryDelay.toMillis());
82 | }
83 |
84 | return null;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsBinding.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.credentials;
2 |
3 | import java.io.IOException;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 |
7 | import org.jenkinsci.Symbol;
8 | import org.jenkinsci.plugins.credentialsbinding.BindingDescriptor;
9 | import org.jenkinsci.plugins.credentialsbinding.MultiBinding;
10 | import org.kohsuke.stapler.DataBoundConstructor;
11 |
12 | import edu.umd.cs.findbugs.annotations.NonNull;
13 | import edu.umd.cs.findbugs.annotations.Nullable;
14 | import hudson.Extension;
15 | import hudson.FilePath;
16 | import hudson.Launcher;
17 | import hudson.model.Run;
18 | import hudson.model.TaskListener;
19 |
20 | public final class BaremetalCloudCredentialsBinding extends MultiBinding {
21 | public static final String ENV_PASSPHRASE = "OCI_PASSPHRASE";
22 | public static final String ENV_USER_ID = "OCI_USER_ID";
23 | public static final String ENV_FINGERPRINT = "OCI_FINGERPRINT";
24 | public static final String ENV_TENANT_ID = "OCI_TENANT_ID";
25 | public static final String ENV_REGION_ID = "OCI_REGION_ID";
26 | public static final String ENV_API_KEY = "OCI_API_KEY";
27 | public static final String ENV_CONFIG_FILE = "OCI_CONFIG_FILE";
28 |
29 | @DataBoundConstructor
30 | public BaremetalCloudCredentialsBinding(String credentialsId) {
31 | super(credentialsId);
32 | }
33 |
34 | @Override
35 | protected Class type() {
36 | return BaremetalCloudCredentials.class;
37 | }
38 |
39 | @Override
40 | public MultiEnvironment bind(
41 | @NonNull Run, ?> build,
42 | @NonNull FilePath workspace,
43 | @Nullable Launcher launcher,
44 | @NonNull TaskListener listener)
45 | throws IOException, InterruptedException {
46 | final BaremetalCloudCredentials credentials = getCredentials(build);
47 | final OciConfigWriter writer = new OciConfigWriter(
48 | credentials.getUserId(),
49 | credentials.getFingerprint(),
50 | credentials.getTenantId(),
51 | credentials.getRegionId(),
52 | credentials.getApikey());
53 | Map secretValues = new HashMap<>();
54 | // Probably unsafe, but may be required. Passphrase is deprecated in OCI config, so will give it to user
55 | // if needed.
56 | secretValues.put(ENV_PASSPHRASE, credentials.getPassphrase());
57 |
58 | return new MultiEnvironment(secretValues, workspace.act(writer.asCallable()));
59 | }
60 |
61 | @Symbol("ociCredentials")
62 | @Extension public static class DescriptorImpl extends BindingDescriptor {
63 | @Override protected Class type() {
64 | return BaremetalCloudCredentials.class;
65 | }
66 |
67 | @Override
68 | public String getDisplayName() {
69 | return "OCI Credentials";
70 | }
71 |
72 | @Override public boolean requiresWorkspace() {
73 | return true;
74 | }
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/ssh/SshConnector.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.ssh;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.logging.Level;
6 | import java.util.logging.Logger;
7 |
8 | import com.trilead.ssh2.Connection;
9 | import com.trilead.ssh2.ConnectionInfo;
10 | import com.trilead.ssh2.KnownHosts;
11 | import com.trilead.ssh2.ServerHostKeyVerifier;
12 |
13 | public class SshConnector {
14 |
15 | private static final Logger LOGGER = Logger.getLogger(SshConnector.class.getName());
16 |
17 | public static Connection createConnection(String host, int port) throws IOException, InterruptedException {
18 | return new Connection(host, port);
19 | }
20 |
21 | public static ConnectionInfo connect(Connection conn, int timeoutMillis, String verificationStrategy) throws IOException {
22 | conn.setTCPNoDelay(true);
23 | if(verificationStrategy.equals("No Verification")) {
24 | LOGGER.log(Level.INFO,"No verification strategy chosen.");
25 | return conn.connect(new NoSshServerHostKeyVerifier(), timeoutMillis, timeoutMillis);
26 | }
27 | LOGGER.log(Level.INFO,"Known host verification strategy chosen.");
28 | return conn.connect(new VerifySshServerHostKeyVerifier(), timeoutMillis, timeoutMillis);
29 | }
30 |
31 | static class NoSshServerHostKeyVerifier implements ServerHostKeyVerifier {
32 | @Override
33 | public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) {
34 | return true;
35 | }
36 | }
37 |
38 | static class VerifySshServerHostKeyVerifier implements ServerHostKeyVerifier {
39 | @Override
40 | public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException, InterruptedException {
41 | KnownHosts knownHosts;
42 | File knownhostsfile = new File("./known_hosts");
43 | if (!knownhostsfile.exists()){
44 | FileCreator fc = new FileCreator();
45 | fc.createfilename="known_hosts";
46 | try {
47 | fc.createFileOnMaster();
48 | } catch(InterruptedException e){
49 | //log.error("Could not create known hosts file, change verification strategy.)
50 | }
51 | knownhostsfile = new File("./known_hosts");
52 | if(!knownhostsfile.exists()){
53 | throw new RuntimeException("No known host file to verify");
54 | }
55 | }
56 | knownHosts = new KnownHosts(knownhostsfile);
57 | int fingerprintMatch = knownHosts.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey);
58 | if (fingerprintMatch == 0 ) {
59 | return true;
60 | } else if (fingerprintMatch == 1) {
61 | String[] hostnames = new String[]{hostname};
62 | KnownHosts.addHostkeyToFile(knownhostsfile,hostnames, serverHostKeyAlgorithm, serverHostKey);
63 | return true;
64 | }
65 | return false;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudInstanceMonitorUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | import hudson.slaves.Cloud;
7 | import org.jmock.Expectations;
8 | import org.junit.Assert;
9 | import org.junit.Rule;
10 | import org.junit.Test;
11 |
12 | import com.oracle.bmc.core.model.Instance;
13 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClient;
14 |
15 | import hudson.model.Node;
16 |
17 | public class BaremetalCloudInstanceMonitorUnitTest {
18 | @Rule
19 | public final BaremetalCloudMockery mockery = new BaremetalCloudMockery();
20 |
21 | static class TestBaremetalCloudInstanceMonitor extends BaremetalCloudInstanceMonitor {
22 | boolean removed;
23 | Node agent;
24 | Cloud bmCloud;
25 |
26 |
27 | TestBaremetalCloudInstanceMonitor(Node agent) {
28 | this.agent = agent;
29 | }
30 |
31 | @Override
32 | protected List getNodes() {
33 | return Arrays.asList(agent);
34 | }
35 |
36 | @Override
37 | protected List getClouds() { return Arrays.asList(bmCloud); }
38 |
39 | @Override
40 | protected void removeNode(BaremetalCloudAgent agent) {
41 | removed = true;
42 | }
43 | }
44 |
45 | private TestBaremetalCloudAgent newBaremetalCloudAgent(Instance.LifecycleState state, final boolean terminate) throws Exception {
46 | final BaremetalCloudClient client = mockery.mock(BaremetalCloudClient.class);
47 | mockery.checking(new Expectations() {{
48 | oneOf(client).getInstanceState("in"); will(returnValue(state));
49 | if (terminate) {
50 | oneOf(client).terminateInstance("in");
51 | oneOf(client).waitForInstanceTerminationToComplete("in");
52 | }
53 | }});
54 | TestBaremetalCloudAgent agent = new TestBaremetalCloudAgent.Builder()
55 | .instanceId("in")
56 | .cloud(new TestBaremetalCloud.Builder().client(client).clock(new TestClock()).build())
57 | .build();
58 | return agent;
59 | }
60 |
61 | @Test
62 | public void testExecuteAlive() throws Exception {
63 | TestBaremetalCloudAgent agent = newBaremetalCloudAgent(Instance.LifecycleState.Running, false);
64 | TestBaremetalCloudInstanceMonitor monitor = new TestBaremetalCloudInstanceMonitor(agent);
65 | monitor.execute(null);
66 | Assert.assertFalse(monitor.removed);
67 | }
68 |
69 | @Test
70 | public void testExecuteNotAlive() throws Exception {
71 | TestBaremetalCloudAgent agent = newBaremetalCloudAgent(Instance.LifecycleState.Stopped, true);
72 | TestBaremetalCloudInstanceMonitor monitor = new TestBaremetalCloudInstanceMonitor(agent);
73 | monitor.execute(null);
74 | Assert.assertTrue(monitor.removed);
75 | }
76 |
77 | @Test
78 | public void testExecuteError() {
79 | TestBaremetalCloudAgent agent = new TestBaremetalCloudAgent.Builder().build();
80 | TestBaremetalCloudInstanceMonitor monitor = new TestBaremetalCloudInstanceMonitor(agent);
81 | monitor.execute(null);
82 | Assert.assertFalse(monitor.removed);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/TestBaremetalCloudAgent.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 |
6 | import hudson.model.Descriptor.FormException;
7 | import hudson.slaves.SlaveComputer;
8 | import jenkins.model.Jenkins;
9 |
10 | @SuppressWarnings("serial")
11 | public class TestBaremetalCloudAgent extends BaremetalCloudAgent {
12 |
13 | public static class Builder {
14 | private String numExecutors;
15 | private String cloudName;
16 | private String instanceId;
17 |
18 | private BaremetalCloud cloud;
19 |
20 | public Builder numExecutors(String numExecutors) {
21 | this.numExecutors = numExecutors;
22 | return this;
23 | }
24 |
25 | public Builder numExecutors(int numExecutors) {
26 | return numExecutors(String.valueOf(numExecutors));
27 | }
28 |
29 | public Builder cloudName(String cloudName) {
30 | this.cloudName = cloudName;
31 | return this;
32 | }
33 |
34 | public Builder instanceId(String instanceId) {
35 | this.instanceId = instanceId;
36 | return this;
37 | }
38 |
39 | public Builder cloud(BaremetalCloud cloud) {
40 | this.cloud = cloud;
41 | return this;
42 | }
43 |
44 | private void appendXml(StringBuilder xml, String name, Object value) {
45 | if (value != null) {
46 | xml.append(" <").append(name).append(">").append(value).append("").append(name).append(">\n");
47 | }
48 | }
49 |
50 | public TestBaremetalCloudAgent build() {
51 | StringBuilder xml = new StringBuilder();
52 | xml.append("\n");
53 | appendXml(xml, "numExecutors", numExecutors);
54 | appendXml(xml, "cloudName", cloudName);
55 | appendXml(xml, "instanceId", instanceId);
56 | xml.append(" ");
57 |
58 | TestBaremetalCloudAgent agent = (TestBaremetalCloudAgent)Jenkins.XSTREAM2.fromXML(xml.toString());
59 | agent.cloud = cloud;
60 | return agent;
61 | }
62 | }
63 |
64 | private BaremetalCloud cloud;
65 |
66 | protected TestBaremetalCloudAgent() throws FormException, IOException {
67 | // Always create an instance by deserializing XML to avoid the Slave
68 | // constructor, which attempts to initialize nodeProperties using
69 | // Jenkins.getInstance(), which is null.
70 | super(
71 | null, // name
72 | null, // template
73 | null, // cloudName
74 | null, // instanceId
75 | null); // host
76 | throw new UnsupportedOperationException();
77 | }
78 |
79 | @Override
80 | protected Object readResolve() {
81 | // Override to avoid Slave.readResolve, which attempts to initialize
82 | // nodeProperties using Jenkins.getInstance(), which is null.
83 | return this;
84 | }
85 |
86 | @Override
87 | public BaremetalCloud getCloud() {
88 | return cloud;
89 | }
90 |
91 | @Override
92 | public SlaveComputer getComputer() {
93 | return null;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudTagsTemplate.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClient;
4 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClientFactory;
5 | import com.oracle.cloud.baremetal.jenkins.client.SDKBaremetalCloudClientFactory;
6 | import com.oracle.cloud.baremetal.jenkins.credentials.BaremetalCloudCredentials;
7 | import hudson.Extension;
8 | import hudson.RelativePath;
9 | import hudson.model.AbstractDescribableImpl;
10 | import hudson.model.Descriptor;
11 | import hudson.util.ListBoxModel;
12 | import org.kohsuke.stapler.QueryParameter;
13 | import org.kohsuke.stapler.export.Exported;
14 | import org.kohsuke.stapler.DataBoundConstructor;
15 |
16 | import java.util.logging.Level;
17 | import java.util.logging.Logger;
18 |
19 | public class BaremetalCloudTagsTemplate extends AbstractDescribableImpl {
20 | private static final Logger LOGGER = Logger.getLogger(BaremetalCloud.class.getName());
21 | private final String namespace;
22 | private final String key;
23 | private final String value;
24 |
25 | @DataBoundConstructor
26 | public BaremetalCloudTagsTemplate(
27 | String namespace,
28 | String key,
29 | String value) {
30 | this.namespace = namespace;
31 | this.key = key;
32 | this.value = value;
33 | }
34 |
35 | @Exported
36 | public String getNamespace() {
37 | return namespace;
38 | }
39 |
40 | @Exported
41 | public String getKey() {
42 | return key;
43 | }
44 |
45 | @Exported
46 | public String getValue() {
47 | return value;
48 | }
49 |
50 | public String toString() {
51 | return key+":"+value;
52 | }
53 |
54 | @Extension
55 | public static class DescriptorImpl extends Descriptor {
56 |
57 | public ListBoxModel doFillNamespaceItems(@QueryParameter @RelativePath("../..") String credentialsId,
58 | @QueryParameter @RelativePath("../..") String maxAsyncThreads,
59 | @QueryParameter @RelativePath("..") String compartmentId) {
60 | ListBoxModel model = new ListBoxModel();
61 | model.add("", "");
62 |
63 | if (credentialsId.isEmpty() || compartmentId.isEmpty()) {
64 | return model;
65 | }
66 |
67 | try {
68 | model.clear();
69 | model.add("None (add a free-form tag)","None");
70 | BaremetalCloudClientFactory factory = SDKBaremetalCloudClientFactory.INSTANCE;
71 | BaremetalCloudClient client = factory.createClient(credentialsId, Integer.parseInt(maxAsyncThreads));
72 | BaremetalCloudCredentials credentials = (BaremetalCloudCredentials) BaremetalCloud.matchCredentials(BaremetalCloudCredentials.class, credentialsId);
73 | if (credentials != null){
74 | compartmentId = credentials.getTenantId();
75 | }
76 | client.getTagNamespaces(compartmentId).stream()
77 | .forEach(n -> model.add(n.getName(), n.getName()));
78 | } catch (Exception e) {
79 | LOGGER.log(Level.WARNING, "Failed to get tag namespaces list", e);
80 | }
81 | return model;
82 |
83 | }
84 |
85 | @Override
86 | public String getDisplayName() {
87 | return "";
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/FormFillFailureUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Assume;
5 | import org.junit.Test;
6 |
7 | import hudson.Util;
8 | import hudson.util.FormValidation;
9 | import hudson.util.ListBoxModel;
10 |
11 | public class FormFillFailureUnitTest {
12 | static { TestMessages.init(); }
13 |
14 | public static ListBoxModel.Option getErrorOption(FormFillFailure e) {
15 | if (e.response instanceof ListBoxModel) {
16 | ListBoxModel model = (ListBoxModel)e.response;
17 | Assert.assertEquals(1, model.size());
18 | return model.get(0);
19 | }
20 |
21 | // We are running with Jenkins 2.50+, so
22 | // hudson.util.FormFillFailure maintains the value.
23 | return null;
24 | }
25 |
26 | public static ListBoxModel.Option assumeGetErrorOption(FormFillFailure e) {
27 | ListBoxModel.Option option = getErrorOption(e);
28 | Assume.assumeNotNull(option);
29 | return option;
30 | }
31 |
32 | @Test(expected = NullPointerException.class)
33 | public void testErrorWithValueNullValue() {
34 | FormFillFailure.errorWithValue("a<>b", null);
35 | }
36 |
37 | @Test
38 | public void testErrorWithValueErrorString() {
39 | Assert.assertEquals(Util.escape("a<>b"), FormFillFailure.errorWithValue("a<>b", "").getMessage());
40 | }
41 |
42 | @Test(expected = IllegalArgumentException.class)
43 | public void testErrorWithValueFormValidationWarning() {
44 | FormFillFailure.errorWithValue(FormValidation.warning("a<>b"), "");
45 | }
46 |
47 | @Test
48 | public void testErrorWithValueFormValidation() {
49 | Assert.assertEquals(Util.escape("a<>b"), FormFillFailure.errorWithValue(FormValidation.error("a<>b"), "").getMessage());
50 | }
51 |
52 | @Test
53 | public void testIsError() {
54 | Assert.assertFalse(FormFillFailure.isError(null));
55 | Assert.assertFalse(FormFillFailure.isError(""));
56 | Assert.assertFalse(FormFillFailure.isError("x"));
57 | }
58 |
59 | @Test
60 | public void testIsErrorWithFallback() {
61 | Assert.assertTrue(FormFillFailure.isError(assumeGetErrorOption(FormFillFailure.errorWithValue("e", "x")).value));
62 | }
63 |
64 | @Test
65 | public void testGetErrorValue() {
66 | Assert.assertNull(FormFillFailure.getErrorValue(null));
67 | Assert.assertEquals("", FormFillFailure.getErrorValue(""));
68 | Assert.assertEquals("x", FormFillFailure.getErrorValue("x"));
69 | }
70 |
71 | @Test
72 | public void testGetErrorValueWithFallback() {
73 | String encodedValue = assumeGetErrorOption(FormFillFailure.errorWithValue("e", "x")).value;
74 | Assert.assertEquals("x", FormFillFailure.getErrorValue(encodedValue));
75 | }
76 |
77 | @Test(expected = IllegalArgumentException.class)
78 | public void testGetErrorValueWithFallbackEncoded() {
79 | String encodedValue = assumeGetErrorOption(FormFillFailure.errorWithValue("e", "x")).value;
80 | Assert.assertEquals("x", FormFillFailure.getErrorValue(assumeGetErrorOption(FormFillFailure.errorWithValue("e", encodedValue)).value));
81 | }
82 |
83 | @Test
84 | public void testValidateRequired() {
85 | Assert.assertEquals(FormValidation.Kind.ERROR, FormFillFailure.validateRequired(null).kind);
86 | Assert.assertEquals(FormValidation.Kind.ERROR, FormFillFailure.validateRequired("").kind);
87 | Assert.assertEquals(FormValidation.Kind.ERROR, FormFillFailure.validateRequired(" ").kind);
88 | Assert.assertEquals(FormValidation.Kind.OK, FormFillFailure.validateRequired("x").kind);
89 | }
90 |
91 | @Test
92 | public void testValidateRequiredWithFallback() {
93 | Assert.assertEquals(FormValidation.Kind.ERROR, FormFillFailure.validateRequired(assumeGetErrorOption(FormFillFailure.errorWithValue("e", "x")).value).kind);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/JenkinsUtil.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import hudson.EnvVars;
4 | import hudson.Util;
5 | import hudson.model.Describable;
6 | import hudson.model.Descriptor;
7 | import hudson.slaves.EnvironmentVariablesNodeProperty;
8 | import hudson.util.FormValidation;
9 | import jenkins.model.Jenkins;
10 |
11 | import java.util.List;
12 |
13 | public class JenkinsUtil {
14 |
15 | //Generic type-safe wrapper for {@link Jenkins#getDescriptorOrDie}.
16 | public static > Descriptor getDescriptorOrDie(Class extends T> type) {
17 | @SuppressWarnings("unchecked")
18 | Descriptor desc = JenkinsUtil.getJenkinsInstance().getDescriptorOrDie(type);
19 | if (!desc.isSubTypeOf(type)) {
20 | throw new IllegalStateException(type.toString());
21 | }
22 | return desc;
23 | }
24 |
25 | // Generic type-safe wrapper for {@link Jenkins#getDescriptorOrDie} to be
26 | // used when a concrete descriptor class is used.
27 | public static , T extends S, D extends Descriptor> D getDescriptorOrDie(Class type, Class descriptorType) {
28 | return descriptorType.cast(JenkinsUtil.getJenkinsInstance().getDescriptorOrDie(type));
29 | }
30 |
31 | /**
32 | * Similar to {@link FormValidation#validateRequired}, but uses the same
33 | * error message as {@code }.
34 | * @param value, the validation value
35 | * @return FormValidation
36 | */
37 | public static FormValidation validateRequired(String value) {
38 | if (Util.fixEmptyAndTrim(value) == null) {
39 | return FormValidation.error(Messages.FormValidation_ValidateRequired());
40 | }
41 | return FormValidation.ok();
42 | }
43 |
44 | private static class Unescaper {
45 | private final String string;
46 | private int pos;
47 | private final StringBuilder builder;
48 |
49 | Unescaper(String string) {
50 | this.string = string;
51 | this.builder = new StringBuilder(string.length());
52 | }
53 |
54 | String unescape() {
55 | while (pos < string.length()) {
56 | if (!unescape(" ", '\n') &&
57 | !unescape("<", '<') &&
58 | !unescape(">", '>') &&
59 | !unescape("&", '&') &&
60 | !unescape(""", '"') &&
61 | !unescape("'", '\'') &&
62 | !unescape(" ", ' ')) {
63 | builder.append(string.charAt(pos));
64 | pos++;
65 | }
66 | }
67 | return builder.toString();
68 | }
69 |
70 | private boolean unescape(String from, char to) {
71 | if (pos + from.length() <= string.length() && string.regionMatches(pos, from, 0, from.length())) {
72 | builder.append(to);
73 | pos += from.length();
74 | return true;
75 | }
76 | return false;
77 | }
78 | }
79 |
80 | //Unescapes a string escaped by {@link Util#escape}.
81 | public static String unescape(String s) {
82 | return new Unescaper(s).unescape();
83 | }
84 |
85 | public static Jenkins getJenkinsInstance() {
86 | Jenkins jenkins = Jenkins.getInstanceOrNull();
87 | if (jenkins == null) {
88 | throw new IllegalStateException("Fail to get Jenkins instance, which means it has not been started, or was already shut down");
89 | }
90 | return jenkins;
91 | }
92 |
93 | public static EnvVars getJenkinsEnvVars() {
94 | List properties = getJenkinsInstance().getGlobalNodeProperties().getAll(EnvironmentVariablesNodeProperty.class);
95 | if (properties.isEmpty()) {
96 | return null;
97 | }
98 | return properties.get(0).getEnvVars();
99 | }
100 | }
101 |
102 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/SshKeyUtil.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.IOException;
5 | import java.io.OutputStream;
6 | import java.io.StringReader;
7 | import java.nio.ByteBuffer;
8 | import java.nio.charset.StandardCharsets;
9 | import java.security.Security;
10 | import java.security.interfaces.RSAPublicKey;
11 |
12 |
13 | import org.apache.commons.codec.binary.Base64;
14 | import org.apache.commons.lang.NotImplementedException;
15 | import org.bouncycastle.jce.provider.BouncyCastleProvider;
16 | import org.bouncycastle.openssl.PEMDecryptorProvider;
17 | import org.bouncycastle.openssl.PEMEncryptedKeyPair;
18 | import org.bouncycastle.openssl.PEMKeyPair;
19 | import org.bouncycastle.openssl.PEMParser;
20 | import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
21 | import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
22 |
23 | public class SshKeyUtil {
24 | private static final String SSH_RSA_ALGORITHM_NAME = "ssh-rsa";
25 |
26 | public static String toSshString(RSAPublicKey key) {
27 | ByteArrayOutputStream out = new ByteArrayOutputStream();
28 | try {
29 | write(out, SSH_RSA_ALGORITHM_NAME.getBytes(StandardCharsets.UTF_8));
30 | write(out, key.getPublicExponent().toByteArray());
31 | write(out, key.getModulus().toByteArray());
32 | } catch (IOException e) {
33 | // Unreachable: ByteArrayOutputStream does not throw IOException.
34 | throw new Error(e);
35 | }
36 |
37 | return SSH_RSA_ALGORITHM_NAME + ' ' + Base64.encodeBase64String(out.toByteArray());
38 | }
39 |
40 | private static void write(ByteArrayOutputStream out, byte[] b) throws IOException {
41 | writeMpint(out, b.length);
42 | out.write(b);
43 | }
44 |
45 | private static void writeMpint(OutputStream out, int value) throws IOException {
46 | out.write((value >> 24) & 0xff);
47 | out.write((value >> 16) & 0xff);
48 | out.write((value >> 8) & 0xff);
49 | out.write(value & 0xff);
50 | }
51 | private static byte[] getSshPublicKeyBody(RSAPublicKey rsaPubKey) throws IOException {
52 | byte[] algorithmName = "ssh-rsa".getBytes("UTF-8");
53 | byte[] algorithmNameLength = ByteBuffer.allocate(4).putInt(algorithmName.length).array();
54 | byte[] e = rsaPubKey.getPublicExponent().toByteArray(); // Usually 65,537
55 | byte[] eLength = ByteBuffer.allocate(4).putInt(e.length).array();
56 | byte[] m = rsaPubKey.getModulus().toByteArray();
57 | byte[] mLength = ByteBuffer.allocate(4).putInt(m.length).array();
58 |
59 | ByteArrayOutputStream os = new ByteArrayOutputStream();
60 | os.write(algorithmNameLength);
61 | os.write(algorithmName);
62 | os.write(eLength);
63 | os.write(e);
64 | os.write(mLength);
65 | os.write(m);
66 |
67 | return os.toByteArray();
68 | }
69 |
70 | public static String getPublicKey(String privateSshKey, String privateSshKeyPassphrase) throws IOException, NotImplementedException {
71 |
72 | PEMKeyPair keyPair;
73 | RSAPublicKey rsaPubKey;
74 | Object keyObj = new PEMParser(new StringReader(privateSshKey)).readObject();
75 |
76 | // Key may be encrypted
77 | if (keyObj instanceof PEMKeyPair) {
78 | keyPair = (PEMKeyPair) keyObj;
79 | } else {
80 | // We need id_hmacWithSHA3_224 for encrypted ssh keys
81 | Security.addProvider(new BouncyCastleProvider());
82 | PEMEncryptedKeyPair encKeyPair = (PEMEncryptedKeyPair) keyObj;
83 | PEMDecryptorProvider decryptionProv = new JcePEMDecryptorProviderBuilder().setProvider("BC").build(privateSshKeyPassphrase.toCharArray());
84 | keyPair = encKeyPair.decryptKeyPair(decryptionProv);
85 | }
86 |
87 | try {
88 | rsaPubKey = (RSAPublicKey) new JcaPEMKeyConverter().getPublicKey(keyPair.getPublicKeyInfo());
89 | } catch (ClassCastException e) {
90 | throw new NotImplementedException("Only RSA SSH keys are currently supported");
91 | }
92 |
93 | byte[] pubKeyBody = getSshPublicKeyBody(rsaPubKey);
94 | String b64PubkeyBody = new String(java.util.Base64.getEncoder().encode(pubKeyBody), "UTF-8");
95 |
96 | return "ssh-rsa " + b64PubkeyBody;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudNsgTemplate.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 |
4 | import com.oracle.bmc.identity.model.Tenancy;
5 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClient;
6 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClientFactory;
7 | import com.oracle.cloud.baremetal.jenkins.client.SDKBaremetalCloudClientFactory;
8 | import hudson.Extension;
9 | import hudson.RelativePath;
10 | import hudson.model.AbstractDescribableImpl;
11 | import hudson.model.Descriptor;
12 | import hudson.util.ListBoxModel;
13 | import org.kohsuke.stapler.QueryParameter;
14 | import org.kohsuke.stapler.export.Exported;
15 | import org.kohsuke.stapler.DataBoundConstructor;
16 |
17 | import javax.servlet.ServletException;
18 | import java.io.IOException;
19 | import java.util.logging.Level;
20 | import java.util.logging.Logger;
21 |
22 | public class BaremetalCloudNsgTemplate extends AbstractDescribableImpl {
23 | private static final Logger LOGGER = Logger.getLogger(BaremetalCloud.class.getName());
24 | private final String nsgCompartmentId;
25 | private final String nsgId;
26 |
27 | @DataBoundConstructor
28 | public BaremetalCloudNsgTemplate(String nsgCompartmentId, String nsgId) {
29 | this.nsgCompartmentId = nsgCompartmentId;
30 | this.nsgId = nsgId;
31 | }
32 |
33 | @Exported
34 | public String getNsgCompartmentId() {
35 | return nsgCompartmentId == null ? "" : nsgCompartmentId;
36 | }
37 |
38 | @Exported
39 | public String getNsgId() {
40 | return nsgId;
41 | }
42 |
43 | public String toString() {
44 | return nsgId;
45 | }
46 |
47 | @Extension
48 | public static class DescriptorImpl extends Descriptor {
49 |
50 | public ListBoxModel doFillNsgCompartmentIdItems(@QueryParameter @RelativePath("../..") String credentialsId,
51 | @QueryParameter @RelativePath("../..") String maxAsyncThreads)
52 | throws IOException, ServletException {
53 | ListBoxModel model = new ListBoxModel();
54 | model.add("", "");
55 |
56 | if (credentialsId.isEmpty()) {
57 | return model;
58 | }
59 |
60 | try{
61 | BaremetalCloudClientFactory factory = SDKBaremetalCloudClientFactory.INSTANCE;
62 | BaremetalCloudClient client = factory.createClient(credentialsId, Integer.parseInt(maxAsyncThreads));
63 | Tenancy tenant = client.getTenant();
64 | model.add(tenant.getName(), tenant.getId());
65 | client.getCompartmentsList().stream()
66 | .forEach(n -> model.add(n.getName(),n.getId()));
67 | }catch (Exception e) {
68 | LOGGER.log(Level.WARNING, "Failed to get compartment list", e);
69 | }
70 | return model;
71 | }
72 |
73 | public ListBoxModel doFillNsgIdItems(@QueryParameter @RelativePath("../..") String credentialsId,
74 | @QueryParameter @RelativePath("../..") String maxAsyncThreads,
75 | @QueryParameter @RelativePath("..") String vcnCompartmentId,
76 | @QueryParameter String nsgCompartmentId) {
77 | ListBoxModel model = new ListBoxModel();
78 | model.add("", "");
79 |
80 | if (credentialsId.isEmpty() || (nsgCompartmentId.isEmpty() && vcnCompartmentId.isEmpty())) {
81 | return model;
82 | }
83 |
84 | //This is needed for upgrade compatibility
85 | if (nsgCompartmentId.isEmpty() && !vcnCompartmentId.isEmpty()){
86 | nsgCompartmentId = vcnCompartmentId;
87 | }
88 | try {
89 | model.clear();
90 | BaremetalCloudClientFactory factory = SDKBaremetalCloudClientFactory.INSTANCE;
91 | BaremetalCloudClient client = factory.createClient(credentialsId, Integer.parseInt(maxAsyncThreads));
92 | client.getNsgIdsList(nsgCompartmentId).stream()
93 | .forEach(n -> model.add(n.getDisplayName(),n.getId()));
94 | } catch (Exception e) {
95 | LOGGER.log(Level.WARNING, "Failed to get NSG list", e);
96 | }
97 | return model;
98 |
99 | }
100 |
101 | @Override
102 | public String getDisplayName() {
103 | return "";
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/credentials/OciConfigWriter.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.credentials;
2 |
3 | import java.io.BufferedWriter;
4 | import java.io.File;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.Serializable;
8 | import java.nio.charset.StandardCharsets;
9 | import java.nio.file.Files;
10 | import java.nio.file.Path;
11 | import java.nio.file.StandardOpenOption;
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | import org.jenkinsci.remoting.RoleChecker;
16 |
17 | import hudson.FilePath.FileCallable;
18 | import hudson.remoting.VirtualChannel;
19 |
20 | public class OciConfigWriter implements Serializable {
21 | public static final String ENV_USER_ID = "OCI_USER_ID";
22 | public static final String ENV_FINGERPRINT = "OCI_FINGERPRINT";
23 | public static final String ENV_TENANT_ID = "OCI_TENANT_ID";
24 | public static final String ENV_REGION_ID = "OCI_REGION_ID";
25 | public static final String ENV_KEY_FILE = "OCI_KEY_FILE";
26 | public static final String ENV_CONFIG_FILE = "OCI_CONFIG_FILE";
27 |
28 | private final String user;
29 | private final String fingerprint;
30 | private final String tenancy;
31 | private final String region;
32 | private final String keyFileContents;
33 |
34 | public OciConfigWriter(String user, String fingerprint, String tenancy, String region, String keyFileContents) {
35 | this.user = user;
36 | this.fingerprint = fingerprint;
37 | this.tenancy = tenancy;
38 | this.region = region;
39 | this.keyFileContents = keyFileContents;
40 | }
41 |
42 | public String getUser() {
43 | return user;
44 | }
45 |
46 | public String getFingerprint() {
47 | return fingerprint;
48 | }
49 |
50 | public String getTenancy() {
51 | return tenancy;
52 | }
53 |
54 | public String getRegion() {
55 | return region;
56 | }
57 |
58 | public String getKeyFileContents() {
59 | return keyFileContents;
60 | }
61 |
62 | public static class Callable implements FileCallable> {
63 | private final OciConfigWriter writer;
64 |
65 | public Callable(final OciConfigWriter writer) {
66 | this.writer = writer;
67 | }
68 |
69 | @Override
70 | public Map invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
71 | return writer.createEnvironment(f);
72 | }
73 |
74 | @Override
75 | public void checkRoles(RoleChecker checker) throws SecurityException {}
76 | }
77 |
78 | public Callable asCallable() {
79 | return new Callable(this);
80 | }
81 |
82 | // Wrap this in its own function so we don't get too nested..
83 | private String getConfigText(final Path keyFile) throws IOException {
84 | try(final InputStream stream = getClass().getResourceAsStream("config.template")) {
85 | return new String(stream.readAllBytes(), StandardCharsets.UTF_8)
86 | .replaceAll("%USER%", user)
87 | .replaceAll("%FINGERPRINT%", fingerprint)
88 | .replaceAll("%TENANCY%", tenancy)
89 | .replaceAll("%REGION%", region)
90 | .replaceAll("%KEY_FILE_PATH%", keyFile.toString());
91 | }
92 | }
93 |
94 | public Map createEnvironment(final File dir) throws IOException {
95 | final Path keyFile = dir.toPath().resolve("oci.pem");
96 | final Path config = dir.toPath().resolve("oci.config");
97 | final String text = getConfigText(keyFile);
98 | try(final BufferedWriter writer =
99 | Files.newBufferedWriter(
100 | keyFile,
101 | StandardOpenOption.WRITE,
102 | StandardOpenOption.CREATE,
103 | StandardOpenOption.TRUNCATE_EXISTING)) {
104 | writer.write(keyFileContents);
105 | }
106 | try(final BufferedWriter writer =
107 | Files.newBufferedWriter(
108 | config,
109 | StandardOpenOption.WRITE,
110 | StandardOpenOption.CREATE,
111 | StandardOpenOption.TRUNCATE_EXISTING)) {
112 | writer.write(text);
113 | }
114 | Map env = new HashMap<>();
115 |
116 | env.put(ENV_USER_ID, getUser());
117 | env.put(ENV_FINGERPRINT, getFingerprint());
118 | env.put(ENV_TENANT_ID, getTenancy());
119 | env.put(ENV_REGION_ID, getRegion());
120 | env.put(ENV_KEY_FILE, keyFile.toString());
121 | env.put(ENV_CONFIG_FILE, config.toString());
122 |
123 | return env;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/resources/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentTemplate/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ${%disableCause(instance.disableCause)}
19 |
20 | ${%reenable}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudAgentUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.io.IOException;
4 | import java.lang.reflect.InvocationTargetException;
5 | import java.lang.reflect.Method;
6 |
7 | import org.jmock.Expectations;
8 | import org.junit.Assert;
9 | import org.junit.Rule;
10 | import org.junit.Test;
11 |
12 | import com.oracle.bmc.core.model.Instance;
13 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClient;
14 |
15 | import hudson.model.TaskListener;
16 | import hudson.slaves.RetentionStrategy.Always;
17 |
18 | public class BaremetalCloudAgentUnitTest {
19 | @Rule
20 | public final BaremetalCloudMockery mockery = new BaremetalCloudMockery();
21 |
22 | private static TaskListener newTerminateTaskListener() {
23 | return null;
24 | }
25 |
26 | @Test
27 | public void testTerminateFromReadyStatus() throws Exception {
28 | final BaremetalCloudClient client = mockery.mock(BaremetalCloudClient.class);
29 | mockery.checking(new Expectations() {{
30 | oneOf(client).terminateInstance("in");
31 | oneOf(client).waitForInstanceTerminationToComplete("in");
32 | }});
33 | TestBaremetalCloudAgent agent = new TestBaremetalCloudAgent.Builder()
34 | .instanceId("in")
35 | .cloud(new TestBaremetalCloud.Builder().client(client).clock(new TestClock()).build())
36 | .build();
37 | agent._terminate(newTerminateTaskListener());
38 | }
39 |
40 | @Test
41 | public void testTerminateFromStoppedStatus() throws Exception {
42 | final BaremetalCloudClient client = mockery.mock(BaremetalCloudClient.class);
43 | mockery.checking(new Expectations() {{
44 | oneOf(client).terminateInstance("in");
45 | oneOf(client).waitForInstanceTerminationToComplete("in");
46 | }});
47 |
48 | TestBaremetalCloudAgent agent = new TestBaremetalCloudAgent.Builder()
49 | .instanceId("in")
50 | .cloud(new TestBaremetalCloud.Builder().client(client).clock(new TestClock()).build())
51 | .build();
52 | agent._terminate(newTerminateTaskListener());
53 | }
54 |
55 | @Test(expected = IOException.class)
56 | public void testTerminateStopError() throws Exception {
57 | final BaremetalCloudClient client = mockery.mock(BaremetalCloudClient.class);
58 | mockery.checking(new Expectations() {{
59 | oneOf(client).terminateInstance("in"); will(throwException(new Exception("test")));
60 | }});
61 | TestBaremetalCloudAgent agent = new TestBaremetalCloudAgent.Builder()
62 | .instanceId("in")
63 | .cloud(new TestBaremetalCloud.Builder().client(client).clock(new TestClock()).build())
64 | .build();
65 | agent._terminate(newTerminateTaskListener());
66 | }
67 |
68 | public void testTerminateCloudNotFound() throws Exception {
69 | TestBaremetalCloudAgent agent = new TestBaremetalCloudAgent.Builder()
70 | .cloudName(BaremetalCloud.NAME_PREFIX + "cn")
71 | .instanceId("in")
72 | .build();
73 | agent._terminate(newTerminateTaskListener());
74 | }
75 |
76 | @Test(expected = IllegalStateException.class)
77 | public void testAliveCloudNotFound() throws Exception {
78 | TestBaremetalCloudAgent agent = new TestBaremetalCloudAgent.Builder()
79 | .instanceId("in")
80 | .build();
81 | agent.isAlive();
82 | }
83 |
84 | @Test
85 | public void testAlive() throws Exception {
86 | final BaremetalCloudClient client = mockery.mock(BaremetalCloudClient.class);
87 | mockery.checking(new Expectations() {{
88 | oneOf(client).getInstanceState("in"); will(returnValue(Instance.LifecycleState.Running));
89 | oneOf(client).getInstanceState("in"); will(returnValue(Instance.LifecycleState.Starting));
90 | oneOf(client).getInstanceState("in"); will(returnValue(Instance.LifecycleState.Provisioning));
91 | oneOf(client).getInstanceState("in"); will(returnValue(Instance.LifecycleState.Stopping));
92 | oneOf(client).getInstanceState("in"); will(returnValue(Instance.LifecycleState.Stopped));
93 | oneOf(client).getInstanceState("in"); will(returnValue(Instance.LifecycleState.Terminated));
94 | oneOf(client).getInstanceState("in"); will(returnValue(Instance.LifecycleState.Terminating));
95 | }});
96 |
97 | TestBaremetalCloudAgent agent = new TestBaremetalCloudAgent.Builder()
98 | .cloud(new TestBaremetalCloud.Builder().client(client).build())
99 | .instanceId("in")
100 | .build();
101 | Assert.assertTrue(agent.isAlive());
102 | Assert.assertTrue(agent.isAlive());
103 | Assert.assertTrue(agent.isAlive());
104 | Assert.assertFalse(agent.isAlive());
105 | Assert.assertFalse(agent.isAlive());
106 | Assert.assertFalse(agent.isAlive());
107 | Assert.assertFalse(agent.isAlive());
108 | }
109 |
110 | @Test
111 | public void testCreateRetentionStrategy() throws IllegalAccessException,
112 | IllegalArgumentException,
113 | InvocationTargetException,
114 | NoSuchMethodException,
115 | SecurityException {
116 |
117 | Method method = BaremetalCloudAgent.class.getDeclaredMethod("createRetentionStrategy", String.class);
118 | method.setAccessible(true);
119 |
120 | Assert.assertTrue(method.invoke(null, "42") instanceof BaremetalCloudRetentionStrategy);
121 | Assert.assertTrue(method.invoke(null, "1") instanceof BaremetalCloudRetentionStrategy);
122 | Assert.assertTrue(method.invoke(null, "-1") instanceof BaremetalCloudRetentionStrategy);
123 | Assert.assertTrue(method.invoke(null, "") instanceof Always);
124 | Assert.assertTrue(method.invoke(null, " ") instanceof Always);
125 | Assert.assertTrue(method.invoke(null, "0") instanceof Always);
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/BaremetalCloudTemplateMonitor.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.io.IOException;
4 | import java.util.List;
5 | import java.util.concurrent.TimeUnit;
6 | import java.util.logging.Level;
7 | import java.util.logging.Logger;
8 |
9 | import com.oracle.bmc.core.model.Image;
10 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClient;
11 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClientFactory;
12 | import com.oracle.cloud.baremetal.jenkins.client.SDKBaremetalCloudClientFactory;
13 |
14 | import hudson.Extension;
15 | import hudson.model.AsyncPeriodicWork;
16 | import hudson.model.TaskListener;
17 | import hudson.slaves.Cloud;
18 |
19 |
20 | @Extension
21 | public class BaremetalCloudTemplateMonitor extends AsyncPeriodicWork{
22 | private static final Logger LOGGER = Logger.getLogger(BaremetalCloudInstanceMonitor.class.getName());
23 | private static final Long recurrencePeriod = TimeUnit.MINUTES.toMillis(3);
24 |
25 | public BaremetalCloudTemplateMonitor() {
26 | super("Oracle Oracle Cloud Infrastructure Compute Templates Monitor");
27 | LOGGER.log(Level.FINE, "Oracle Cloud Infrastructure Compute Templates Monitor check period is {0}ms", recurrencePeriod);
28 | }
29 |
30 | @Override
31 | protected void execute(TaskListener taskListener) throws IOException {
32 |
33 | for (Cloud c : JenkinsUtil.getJenkinsInstance().clouds) {
34 | if (c instanceof BaremetalCloud) {
35 | BaremetalCloud cloud = (BaremetalCloud) c;
36 |
37 | for (BaremetalCloudAgentTemplate template: cloud.getTemplates()) {
38 |
39 | if(template.isTemplateSleep()) {
40 | long retryTimeOutMins = TimeUnit.MINUTES.toMillis(template.getRetryTimeoutMins());
41 | LOGGER.log(Level.INFO,"Monitoring sleeping template " + template.getDisplayName()
42 | + " provided retryTime "+ template.getRetryTimeoutMins()+" minutes.");
43 | long differenceTime = System.currentTimeMillis()-template.getSleepStartTime();
44 | if (differenceTime > retryTimeOutMins){
45 | template.setTemplateSleep(false);
46 | if(template.getDisableCause()==null) {
47 | LOGGER.log(Level.INFO, "Template {0} is available for provisioning now.", template.getDisplayName());
48 | } else {
49 | LOGGER.log(Level.INFO, "Template {0} is disabled after encountering 20 failures.", template.getDisplayName());
50 | }
51 |
52 | } else {
53 | if(template.getDisableCause()==null){
54 | LOGGER.log(Level.INFO,"Not yet available, wait for atleast {0} minutes.",
55 | (TimeUnit.MILLISECONDS.toMinutes(retryTimeOutMins-differenceTime)+1));
56 | }
57 | }
58 | }
59 |
60 | if (template.getAutoImageUpdate()) {
61 | String imageId = template.getImageId();
62 |
63 | BaremetalCloudClientFactory factory = SDKBaremetalCloudClientFactory.INSTANCE;
64 | BaremetalCloudClient client = factory.createClient(cloud.getCredentialsId(), Integer.parseInt(cloud.getMaxAsyncThreads()));
65 |
66 | try {
67 | List images = client.getImagesList(template.getImageCompartmentId());
68 |
69 | for (Image image : images) {
70 | if (image.getId().equals(imageId)) {
71 | String imageName = image.getDisplayName();
72 |
73 | for (Image image2 : images) {
74 | if (image2.getDisplayName().equals(imageName)
75 | && !image2.getId().equals(imageId)
76 | && image2.getTimeCreated().compareTo(image.getTimeCreated()) > 0) {
77 | LOGGER.log(Level.INFO, "A new version of the image {0} was found. It is used in the template.", imageName);
78 | template.setImageId(image2.getId());
79 | }
80 | }
81 |
82 | }
83 | }
84 |
85 | } catch (Exception e) {
86 | LOGGER.log(Level.WARNING, "Failed to get images list", e);
87 | }
88 | }
89 | }
90 | }
91 | }
92 | }
93 |
94 | @Override
95 | public long getRecurrencePeriod() {
96 | return recurrencePeriod;
97 | }
98 |
99 | private BaremetalCloudAgentTemplate createNewTemplate(BaremetalCloudAgentTemplate oldTemplate, String newImageId) {
100 | return new BaremetalCloudAgentTemplate(
101 | oldTemplate.compartmentId,
102 | oldTemplate.availableDomain,
103 | oldTemplate.vcnCompartmentId,
104 | oldTemplate.vcnId,
105 | oldTemplate.subnetCompartmentId,
106 | oldTemplate.subnetId,
107 | oldTemplate.nsgIds,
108 | oldTemplate.imageCompartmentId,
109 | newImageId,
110 | oldTemplate.shape,
111 | oldTemplate.sshCredentialsId,
112 | oldTemplate.description,
113 | oldTemplate.remoteFS,
114 | oldTemplate.assignPublicIP,
115 | oldTemplate.usePublicIP,
116 | oldTemplate.numExecutors,
117 | oldTemplate.mode,
118 | oldTemplate.labelString,
119 | oldTemplate.idleTerminationMinutes,
120 | oldTemplate.templateId,
121 | oldTemplate.jenkinsAgentUser,
122 | oldTemplate.customJavaPath,
123 | oldTemplate.customJVMOpts,
124 | oldTemplate.initScript,
125 | oldTemplate.getExportJenkinsEnvVars(),
126 | oldTemplate.sshConnectTimeoutSeconds,
127 | oldTemplate.verificationStrategy,
128 | oldTemplate.startTimeoutSeconds,
129 | oldTemplate.initScriptTimeoutSeconds,
130 | oldTemplate.instanceCap,
131 | oldTemplate.numberOfOcpus,
132 | oldTemplate.getAutoImageUpdate(),
133 | oldTemplate.getStopOnIdle(),
134 | oldTemplate.getTags(),
135 | oldTemplate.getInstanceNamePrefix(),
136 | oldTemplate.getMemoryInGBs(),
137 | oldTemplate.getDoNotDisable(),
138 | oldTemplate.retryTimeoutMins
139 | );
140 |
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.credentials;
2 |
3 | import java.util.logging.Logger;
4 | import org.kohsuke.stapler.DataBoundConstructor;
5 | import org.kohsuke.stapler.QueryParameter;
6 |
7 | import com.cloudbees.plugins.credentials.CredentialsScope;
8 | import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials;
9 | import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider;
10 | import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider;
11 | import com.oracle.bmc.model.BmcException;
12 |
13 | import com.oracle.cloud.baremetal.jenkins.client.BaremetalCloudClient;
14 | import com.oracle.cloud.baremetal.jenkins.client.SDKBaremetalCloudClient;
15 | import hudson.Extension;
16 | import hudson.util.FormValidation;
17 | import hudson.util.Secret;
18 | import java.io.ByteArrayInputStream;
19 | import java.nio.charset.StandardCharsets;
20 | import java.util.logging.Level;
21 |
22 | public final class BaremetalCloudCredentialsImpl extends BaseStandardCredentials implements BaremetalCloudCredentials {
23 | private static final Logger LOGGER = Logger.getLogger(BaremetalCloudCredentials.class.getName());
24 |
25 | private final String fingerprint;
26 | private final String apikey;
27 | private final String passphrase;
28 | private final String tenantId;
29 | private final String userId;
30 | private final String regionId;
31 | private final boolean instancePrincipals;
32 |
33 | @DataBoundConstructor
34 | public BaremetalCloudCredentialsImpl(CredentialsScope scope,
35 | String id,
36 | String description,
37 | String fingerprint,
38 | String apikey,
39 | String passphrase,
40 | String tenantId,
41 | String userId,
42 | String regionId,
43 | boolean instancePrincipals) {
44 | super(scope, id, description);
45 | this.fingerprint = fingerprint;
46 | this.apikey = getEncryptedValue(apikey);
47 | this.passphrase = getEncryptedValue(passphrase);
48 | this.tenantId = tenantId;
49 | this.userId = userId;
50 | this.regionId = regionId;
51 | this.instancePrincipals = instancePrincipals;
52 | }
53 |
54 | @Override
55 | public String getFingerprint() {
56 | return fingerprint;
57 | }
58 |
59 | @Override
60 | public String getApikey() {
61 | return getPlainText(apikey);
62 | }
63 |
64 | @Override
65 | public String getPassphrase() {
66 | return getPlainText(passphrase);
67 | }
68 |
69 | @Override
70 | public String getTenantId() {
71 | return tenantId;
72 | }
73 |
74 | @Override
75 | public String getUserId() {
76 | return userId;
77 | }
78 |
79 | @Override
80 | public String getRegionId() {
81 | return regionId;
82 | }
83 |
84 | @Override
85 | public boolean isInstancePrincipals() {
86 | return instancePrincipals;
87 | }
88 |
89 | protected String getEncryptedValue(String str) {
90 | return Secret.fromString(str).getEncryptedValue();
91 | }
92 |
93 | protected String getPlainText(String str) {
94 | if (str != null) {
95 | Secret secret = Secret.decrypt(str);
96 | if (secret != null) {
97 | return secret.getPlainText();
98 | }
99 | }
100 | return null;
101 | }
102 |
103 | @Extension
104 | public static class DescriptorImpl extends BaseStandardCredentials.BaseStandardCredentialsDescriptor {
105 |
106 | @Override
107 | public String getDisplayName() {
108 | return "Oracle Cloud Infrastructure Credentials";
109 | }
110 |
111 | public FormValidation doTestConnection(
112 | @QueryParameter String fingerprint,
113 | @QueryParameter String apikey,
114 | @QueryParameter String passphrase,
115 | @QueryParameter String tenantId,
116 | @QueryParameter String userId,
117 | @QueryParameter String regionId,
118 | @QueryParameter boolean instancePrincipals) {
119 | if (!instancePrincipals) {
120 | SimpleAuthenticationDetailsProvider provider = SimpleAuthenticationDetailsProvider.builder()
121 | .fingerprint(fingerprint)
122 | .passPhrase(passphrase)
123 | .privateKeySupplier(() -> new ByteArrayInputStream(apikey.getBytes(StandardCharsets.UTF_8)))
124 | .tenantId(tenantId)
125 | .userId(userId)
126 | .build();
127 | BaremetalCloudClient client = new SDKBaremetalCloudClient(provider, regionId, 50);
128 | try{
129 | client.authenticate();
130 | return FormValidation.ok(com.oracle.cloud.baremetal.jenkins.Messages.BaremetalCloud_testConnection_success());
131 | }catch(BmcException e){
132 | LOGGER.log(Level.INFO, "Failed to connect to Oracle Cloud Infrastructure. Please verify all the credential information entered.", e);
133 | return FormValidation.error(com.oracle.cloud.baremetal.jenkins.Messages.BaremetalCloud_testConnection_unauthorized());
134 | }
135 | } else {
136 | InstancePrincipalsAuthenticationDetailsProvider provider = InstancePrincipalsAuthenticationDetailsProvider.builder().build();
137 | BaremetalCloudClient client = new SDKBaremetalCloudClient(provider, regionId, 50, tenantId);
138 | try{
139 | // If using Instance Principals, other credentials should not be
140 | // present.
141 | if (fingerprint != null && !fingerprint.trim().isEmpty()) {
142 | LOGGER.log(Level.INFO, "Fingerprint ignored when using Instance Principals");
143 | }
144 | if (apikey != null && !apikey.trim().isEmpty()) {
145 | LOGGER.log(Level.INFO, "API Key ignored when using Instance Principals");
146 | }
147 | if (passphrase != null && !passphrase.trim().isEmpty()) {
148 | LOGGER.log(Level.INFO, "Passphrase ignored when using Instance Principals");
149 | }
150 | if (userId != null && !userId.trim().isEmpty()) {
151 | LOGGER.log(Level.INFO, "User ID ignored when using Instance Principals");
152 | }
153 |
154 | client.authenticate();
155 | return FormValidation.ok(com.oracle.cloud.baremetal.jenkins.Messages.BaremetalCloud_testConnection_success());
156 | }catch(BmcException e){
157 | LOGGER.log(Level.INFO, "Failed to connect to Oracle Cloud Infrastructure using Instance Principals. Please verify all the credential information entered.", e);
158 | return FormValidation.error(com.oracle.cloud.baremetal.jenkins.Messages.BaremetalCloud_testConnection_unauthorized());
159 | }
160 | }
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/client/BaremetalCloudClient.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins.client;
2 |
3 | import java.util.List;
4 |
5 | import com.oracle.bmc.core.model.*;
6 | import com.oracle.bmc.core.responses.GetSubnetResponse;
7 | import com.oracle.bmc.identity.model.AvailabilityDomain;
8 | import com.oracle.bmc.identity.model.Compartment;
9 | import com.oracle.bmc.identity.model.TagNamespaceSummary;
10 | import com.oracle.bmc.identity.model.Tenancy;
11 | import com.oracle.bmc.model.BmcException;
12 | import com.oracle.cloud.baremetal.jenkins.BaremetalCloudAgentTemplate;
13 |
14 | public interface BaremetalCloudClient extends AutoCloseable {
15 |
16 | /**
17 | * @throws BmcException if an error occurs
18 | */
19 | void authenticate() throws BmcException;
20 |
21 |
22 | /**
23 | * Creates an instance with the specified template
24 | *
25 | * @param template the instance configuration
26 | * @param name instance name
27 | * @return Instance
28 | * @throws Exception if an error occurs
29 | */
30 | Instance createInstance(String name, BaremetalCloudAgentTemplate template) throws Exception;
31 |
32 | /**
33 | * Creates an instance with the specified instance id
34 | *
35 | * @param instanceId the instance id created before
36 | * @return Instance
37 | * @throws Exception if an error occurs
38 | */
39 | Instance waitForInstanceProvisioningToComplete(String instanceId) throws Exception;
40 |
41 | /**
42 | * Get an instance public or private ip with the specified instance id
43 | *
44 | * @param instanceId the instance id created before
45 | * @param template baremetal cloud agent template
46 | * @return the instance ip
47 | * @throws Exception if an error occurs
48 | */
49 | String getInstanceIp(BaremetalCloudAgentTemplate template, String instanceId) throws Exception;
50 |
51 | /**
52 | * Get the compartment list
53 | *
54 | * @return compartment list
55 | * @throws Exception if an error occurs
56 | */
57 | List getCompartmentsList() throws Exception;
58 |
59 | /**Get root compartment
60 | * @return tenancy
61 | * @throws Exception if an error occurs
62 | */
63 | Tenancy getTenant() throws Exception;
64 |
65 | /**
66 | * Get the available domain response
67 | *
68 | * @param compartmentId the compartment id
69 | * @return ad list
70 | * @throws Exception if an error occurs
71 | */
72 | List getAvailabilityDomainsList(String compartmentId) throws Exception;
73 |
74 | /**
75 | * Get the available image list
76 | *
77 | * @param compartmentId the compartment id
78 | * @return image list
79 | * @throws Exception if an error occurs
80 | */
81 | List getImagesList(String compartmentId) throws Exception;
82 |
83 | /**
84 | * Get the shape list
85 | *
86 | * @param compartmentId the compartment id
87 | * @param availableDomain available domain
88 | * @param imageId image id
89 | * @return shape list
90 | * @throws Exception if an error occurs
91 | */
92 | List getShapesList(String compartmentId, String availableDomain, String imageId) throws Exception;
93 |
94 | /**
95 | * Get the OCPUs options
96 | *
97 | * @param compartmentId the compartment id
98 | * @param availableDomain available domain
99 | * @param imageId image id
100 | * @param shape shape name
101 | * @return an array with min (array[0]) and max (array[1])
102 | * @throws Exception if an error occurs
103 | */
104 | Integer[] getMinMaxOcpus(String compartmentId, String availableDomain, String imageId, String shape) throws Exception;
105 |
106 | /**
107 | * Get the memory options of Flex shapes
108 | *
109 | * @param compartmentId the compartment id
110 | * @param availableDomain available domain
111 | * @param imageId image id
112 | * @param shape shape name
113 | * @return an array with min (array[0]) and max (array[1])
114 | * @throws Exception if an error occurs
115 | */
116 | Integer[] getMinMaxMemory(String compartmentId, String availableDomain, String imageId, String shape) throws Exception;
117 | /**
118 | * Get the Virtual Cloud Network list
119 | *
120 | * @param tenantId the tenant id
121 | * @return vcn list
122 | * @throws Exception if an error occurs
123 | */
124 | List getVcnList(String tenantId) throws Exception;
125 |
126 | /**
127 | * Get the sub net list
128 | *
129 | * @param tenantId the tenant id
130 | * @param vcnId vcn id
131 | * @return subnet list
132 | * @throws Exception if an error occurs
133 | */
134 | List getSubNetList(String tenantId, String vcnId) throws Exception;
135 |
136 | /**
137 | * Get the network security group list
138 | *
139 | * @param compartmentId the compartment id
140 | * @return network security group list
141 | * @throws Exception if an error occurs
142 | */
143 | List getNsgIdsList(String compartmentId) throws Exception;
144 |
145 | /**
146 | * Get the sub net
147 | *
148 | * @param subnetId subnet id
149 | * @return GetSubnetResponse
150 | * @throws Exception if an error occurs
151 | */
152 | GetSubnetResponse getSubNet(String subnetId) throws Exception;
153 |
154 | /**
155 | * Terminate instance with the specified instance id
156 | *
157 | * @param instanceId the instance id created before
158 | * @return Opc Request Id
159 | * @throws Exception if an error occurs
160 | */
161 | String terminateInstance(String instanceId) throws Exception;
162 |
163 | /**
164 | * Wait for the termination with the specified instance id to complete
165 | *
166 | * @param instanceId the instance id created before
167 | * @return Instance
168 | * @throws Exception if an error occurs
169 | */
170 | Instance waitForInstanceTerminationToComplete(String instanceId) throws Exception;
171 |
172 | /**
173 | * Check instance's state
174 | *
175 | * @param instanceId the instance id created before
176 | * @return Instance.LifecycleState
177 | * @throws Exception if an error occurs
178 | */
179 | Instance.LifecycleState getInstanceState(String instanceId) throws Exception;
180 |
181 | /**
182 | * Get a list of stopped instances on OCI
183 | *
184 | * @param compartmentId the compartment id
185 | * @param availableDomain available domain
186 | * @return Instance.LifecycleState
187 | * @throws Exception if an error occurs
188 | */
189 | List getStoppedInstances(String compartmentId, String availableDomain) throws Exception;
190 |
191 | /**
192 | * Stop instance with the specified instance id
193 | *
194 | * @param instanceId the instance id created before
195 | * @return Instance.LifecycleState
196 | * @throws Exception if an error occurs
197 | */
198 | String stopInstance(String instanceId) throws Exception;
199 |
200 | /**
201 | * Start instance with the specified instance id
202 | *
203 | * @param instanceId the instance id created before
204 | * @return Instance.LifecycleState
205 | * @throws Exception if an error occurs
206 | */
207 | Instance startInstance(String instanceId) throws Exception;
208 |
209 | /**
210 | * Start instance with the specified instance id
211 | *
212 | * @param compartmentId the compartment id
213 | * @return tag namespace summary list
214 | * @throws Exception if an error occurs
215 | */
216 | List getTagNamespaces(String compartmentId) throws Exception;
217 | }
218 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](http://keepachangelog.com/).
5 |
6 | ## 1.0.18 - July 2024
7 |
8 | ### Added
9 | - JDK 11
10 | - Credential binding so BaremetalCloudCredentials can be used in jenkinsfiles
11 |
12 |
13 | ### Fixed
14 | - Do not bring agent online if init script fails with non-zero return code
15 | - Use login shell for non opc user
16 | - Several dependencies updated to date
17 | - Removed prototype usage in jelly scripts
18 | - Avoid reloading instance templates in template monitor
19 |
20 | ## 1.0.17 - May 2023
21 |
22 | ### Added
23 |
24 | - OCI Java SDK 2.46.0
25 | - Credentials Plugin 2.6.1.1
26 | - Secure verification strategy for SSH Connection.
27 | - Custom Java options for agent.
28 | - Remoting options for debugging and caching.
29 |
30 |
31 | ### Fixed
32 |
33 | - Exclude jersey inside OCI SDK to prevent conflicts with plugins using jersey2.
34 | - Fix hangs when deleting slave.jar on Windows agents.
35 | - Improved logging on instance failure.
36 |
37 | ## 1.0.16 - May 2022
38 |
39 | ### Added
40 |
41 | - OCI Java SDK 2.27.0
42 | - Switch between multiple templates for provisioning.
43 | - Template Retry option
44 | - Logs for monitoring instances limit
45 | - No deletion of stopped instances upon failures
46 |
47 |
48 | ### Fixed
49 |
50 | - Auto Image update option breaks the cloud config.
51 | - NPE in SSHComputerLauncher when InitScript is empty.
52 | - 'Delete Agent' redirect to homepage.
53 |
54 | ## 1.0.15 - November 2021
55 |
56 | ### Added
57 |
58 | - OCI Java SDK 2.5.1
59 | - Credentials Plugin 2.3.19
60 | - Custom Agent User
61 |
62 |
63 | ### Fixed
64 |
65 | - Error reporting - print original stack trace.
66 | - Details on Instance Principals option.
67 |
68 | ## 1.0.14 - April 2021
69 |
70 | ### Added
71 |
72 | - OCI Java SDK 1.36.0
73 |
74 | ### Fixed
75 |
76 | - Instance Termination if Jenkins tries to reconnect node while its Stopping
77 | - Terminating log string is ambiguous
78 |
79 |
80 |
81 | ## 1.0.13 - February 2021
82 |
83 | ### Added
84 |
85 | - OCI Java SDK 1.30.0
86 | - Masking of OCI Credentials
87 | - Explicit multiple Instance Provisioning
88 |
89 | ### Fixed
90 |
91 | - Stop/Start Node's name not matching Instance's name
92 | - Stop/Start Option not affecting already created Nodes
93 |
94 |
95 |
96 | ## 1.0.12 - January 2021
97 |
98 | ### Added
99 |
100 | - OCI Java SDK 1.29.0
101 | - Export Jenkins Variables to Init Script
102 | - **Memory in GBs** option for Flex Shapes
103 | - Network Security Groups Compartment option
104 |
105 | ### Fixed
106 |
107 | - java.lang.NumberFormatException: For input string: ""
108 | - NPE in SDKBaremetalCloudClient.getTenant() when using Instance Principals
109 | - Only the last Tag is set when setting more than one Tag
110 |
111 |
112 |
113 | ## 1.0.11 - November 2020
114 |
115 | ### Added
116 |
117 | - OCI Java SDK 1.26.0
118 | - Tags Template option
119 | - Custom Instance Name Prefix option
120 | - root compartment added to Compartment list
121 | - Images added to the Stop/Start filter
122 |
123 |
124 |
125 | ## 1.0.10 - September 2020
126 |
127 | ### Fixed
128 |
129 | - Fix java.util.NoSuchElementException: No value present
130 |
131 |
132 |
133 | ### 1.0.9 - September 2020
134 |
135 | ### Added
136 |
137 | - Network **Subnet Compartment** field.
138 | - **Network Security Groups** field.
139 | - **Identical Named Images** checkbox to automatically select the newest Image if multiple Images exist with same name.
140 | - **Stop on Idle Timeout** checkbox so an instance is stopped and not terminated when the Idle timeout expires.
141 | - Log if an Instance was created via Jenkins Job label or via Jenkins Nodes.
142 |
143 | ## 1.0.8 - July 2020
144 |
145 | ### Added
146 |
147 | - OCI Java SDK 1.19.2
148 | - Support for E3/Flex Compute Shapes. **Note:** After upgrade please check all OCI Cloud values are OK in Manage Jenkins > Manage Nodes and Clouds > Configure Clouds. Then Click **Save**.
149 | - Improvements in Instance Termination behavior.
150 |
151 | ### Fixed
152 |
153 | - Fix in Exception handling to avoid dangling instances.
154 |
155 | ## 1.0.7 - June 2020
156 |
157 | ### Added
158 |
159 | - OCI Java SDK 1.17.4
160 |
161 | ## 1.0.6 - September 2019
162 |
163 | ### Added
164 |
165 | - OCI API keys and SSH Keys are now defined in Jenkins Credentials. **Note:** if upgrading you need to update the values in your existing Cloud configuration(s).
166 | - Support for Instance Principals and calling services from an Instance. See [Calling Services from an Instance](https://docs.cloud.oracle.com/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm) documentation for additional information.
167 | - OCI Java SDK 1.7.0
168 |
169 | ## 1.0.5 - July 2019
170 |
171 | ### Added
172 | - Jenkins Master's IP Address to the Instance Names in OCI i.e. jenkins-**12.191.12.125**-182258c6-7dc7-4d8c-acce-1a292a56cfaa.
173 | - Regional subnets in the Virtual Networking service support.
174 | - OCI Java SDK 1.5.11
175 |
176 | ### Changed
177 | - Default values for **Instance Creation Timeout** and **Instance SSH Connection Timeout** to 900.
178 |
179 | ### Fixed
180 | - OCI Slaves removing from Jenkins when Jenkins loses Network Connectivity.
181 |
182 |
183 | ## 1.0.4 - November 2018
184 | ### Fixed
185 | - Compartments listed are no longer limited to 25 values.
186 | - Child Compartments are now visible in Compartments.
187 |
188 | ### Added
189 | - Template Instance Cap functionality. Instance Cap can now be placed at Template level.
190 | - "Virtual Cloud Network Compartment" Drop Down in Template configuration to access Network resources in separate compartments. Improves Template loading performance. **Note:** if upgrading from v1.0.3 (or earlier) and the Networks resources is in a separate compartment than the default Compartment, you may have to update the values in your existing Template configuration.
191 |
192 | ## 1.0.3 - October 2018
193 | ### Fixed
194 | - Fix "I/O error in channel oci-compute" java.io.EOFException severe messages in Jenkins log.
195 | - Fix issue where some values fail due to OCI API limit being exceeded with large number of Templates.
196 |
197 | ### Changed
198 | - Plugin Description seen in Plugin's Available screen.
199 |
200 | ### Added
201 | - "Max number of async threads" Field in Cloud configuration. Allows user to specify the max number of async threads to use when loading Templates configuration.
202 | - "Image Compartment" Drop Down in Template configuration for images in separate compartments. **Note:** if upgrading from v1.0.2 (or earlier) and the Images are in a separate compartment than the default Compartment, you may have to update the values in your existing Template configuration.
203 |
204 |
205 | ## 1.0.2 - June 2018
206 | ### Fixed
207 | - Instance cap can no longer be exceeded
208 | - Fix error on Node Configuration Screen
209 |
210 | ### Changed
211 | - Subnets now filtering by Availability Domain
212 | - Use Jenkins HTTP proxy configuration for OCI API calls
213 | - Prevent termination of temporarily offline Agents
214 |
215 | ### Added
216 | - Faster loading of Cloud and Template configuration options in Jenkins Configure screen
217 | - Better error description for remote machine with no Java installed
218 | - "Name" and "Number of Executors" reconfiguration options in the Nodes > Configure Screen
219 |
220 | ## 1.0.1 - April 2018
221 | ### Fixed
222 |
223 | - Idle Termination Minutes. 0 now working as expected and Instance will not Terminate.
224 |
225 | - Fixed broken links in Plugin Help options.
226 |
227 |
228 | - Fixed "unexpected stream termination" issue which removes HTTP Proxy for ssh connection to agents.
229 | - ssh credentials are now encrypted in Jenkins config file.
230 |
231 |
232 | ### Changed
233 | - Shorten Compartment Drop-Down names and removed bloated bracket content.
234 |
235 | ### Added
236 | - Ability to access Images, Virtual Cloud Network, and Subnet items from separate Compartments.
237 |
238 | - Checkbox to attach Public IP to Instances. If this option is unchecked, only the private IP is assigned.
239 |
240 |
241 | - Checkbox to use Public IP to ssh to instances. If this Option is unchecked, the Plugin will connect to the private IP of the instance.
242 |
243 | ## 1.0.0 - December 2017
244 | ### Added
245 | - Initial Release
246 | - Support added for OCI resource allocation via Jenkins plugin
247 |
--------------------------------------------------------------------------------
/src/main/java/com/oracle/cloud/baremetal/jenkins/FormFillFailure.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.io.IOException;
4 | import java.lang.reflect.Method;
5 | import java.util.Objects;
6 |
7 | import javax.servlet.ServletException;
8 |
9 | import org.kohsuke.stapler.HttpResponse;
10 | import org.kohsuke.stapler.StaplerRequest;
11 | import org.kohsuke.stapler.StaplerResponse;
12 |
13 | import hudson.Util;
14 | import hudson.util.FormValidation;
15 | import hudson.util.ListBoxModel;
16 |
17 | /**
18 | * Helper for {@code hudson.util.FormFillFailure}, which was not added until
19 | * JENKINS-42443 ,
20 | * which was added in Jenkins 2.50. If an older version of Jenkins is being
21 | * used at runtime, then a fallback implementation is used.
22 | *
23 | * Sample usage
24 | *
25 | * public class TheClass {
26 | * {@literal @}DataBoundConstructor
27 | * public TheClass(String theField) {
28 | * // Decode in case the control form had an encoded error.
29 | * this.theField = FormFillFailure.getErrorValue(theField);
30 | * }
31 | *
32 | * public class DescriptorImpl ... {
33 | * public ListBoxModel doFillTheFieldItems({@literal @}QueryParameter String theField) throws FormFillFailure {
34 | * // Decode in case the control form had an encoded error.
35 | * theField = FormFillFailure.getErrorValue(theField);
36 | * if (...error...) {
37 | * throw FormFillFailure.error(...message..., theField);
38 | * }
39 | *
40 | * // Add an empty value to require selection.
41 | * ListBoxModel model = new ListBoxModel().add("");
42 | *
43 | * // Explicitly set the selected parameter in case the control form
44 | * // value had an encoded error with this value.
45 | * model.add(new ListBoxModel.Option(displayName, value, value.equals(theField)));
46 | *
47 | * return model;
48 | * }
49 | *
50 | * public FormValidation doCheckTheField({@literal @}QueryParameter String theField) {
51 | * // Decode in case the control form had an encoded error without
52 | * // a selected value.
53 | * return FormFillFailure.validateRequired(value);
54 | * }
55 | * }
56 | * }
57 | *
58 | */
59 | @SuppressWarnings("serial")
60 | public class FormFillFailure extends IOException implements HttpResponse {
61 | // This class can be tested using: mvn -Djenkins.version=2.50
62 |
63 | /**
64 | * The {@code hudson.util.FormFillFailure.error(String)} method, or null if
65 | * the fallback implementation should be used.
66 | */
67 | private static final Method FORM_FILL_FAILURE_ERROR_METHOD = getFormFillFailureErrorMethod();
68 |
69 | private static Method getFormFillFailureErrorMethod() {
70 | try {
71 | Class extends HttpResponse> c = Class.forName("hudson.util.FormFillFailure").asSubclass(HttpResponse.class);
72 | return c.getMethod("error", String.class);
73 | } catch (ClassNotFoundException |NoSuchMethodException e) {
74 | return null;
75 | }
76 | }
77 |
78 | private static final String ERROR_VALUE_PREFIX = FormFillFailure.class.getName() + ".ERROR:";
79 |
80 | private static FormFillFailure errorWithValue(String message, Throwable cause, String value) {
81 | Objects.requireNonNull(value, "value");
82 |
83 | HttpResponse response = null;
84 | if (FORM_FILL_FAILURE_ERROR_METHOD != null) {
85 | try {
86 | response = (HttpResponse)FORM_FILL_FAILURE_ERROR_METHOD.invoke(null, message);
87 | } catch (Exception e) {
88 | throw new IllegalStateException(e);
89 | }
90 | } else {
91 | // Use the fallback implementation, which is to create a single
92 | // option with a display name that shows an error, and an encoded
93 | // value that we can detect later.
94 |
95 | // If the form control value is already an encoded error,
96 | if (isError(value)) {
97 | throw new IllegalArgumentException();
98 | }
99 |
100 | ListBoxModel model = new ListBoxModel();
101 | model.add(Messages.FormFillFailure_error(message), ERROR_VALUE_PREFIX + value);
102 | response = model;
103 | }
104 |
105 | return new FormFillFailure(Util.escape(message), cause, response);
106 | }
107 |
108 | /**
109 | * Creates a new {@code FormFillFailure} with the specified message and with
110 | * the specified value. The value will be encoded in the form control value
111 | * so that both the error status and original value can be determined.
112 | *
113 | * @param message the error message
114 | * @param value the value when the error occurred
115 | *
116 | * @return the {@code FormFillFailure} created
117 | *
118 | * @throws IllegalArgumentException if the value was already encoded by this
119 | * method
120 | * @see #getErrorValue
121 | */
122 | public static FormFillFailure errorWithValue(String message, String value) {
123 | return errorWithValue(message, null, value);
124 | }
125 |
126 | /**
127 | * Calls {@link #errorWithValue(String, String)} with the message from the
128 | * specified {@code FormValidation}. The kind must be
129 | * {@code FormValidation.Kind.ERROR}.
130 | *
131 | * @param fv the cause of the failure
132 | * @param value the value when the error occurred
133 | *
134 | * @return the {@code FormFillFailure} created
135 | *
136 | * @throws IllegalArgumentException if the kind is not ERROR
137 | * @throws IllegalArgumentException if the value was already encoded by
138 | * {@link #errorWithValue(String, String)}
139 | * @see #getErrorValue
140 | */
141 | public static FormFillFailure errorWithValue(FormValidation fv, String value) {
142 | if (fv.kind != FormValidation.Kind.ERROR) {
143 | throw new IllegalArgumentException("kind " + fv.kind);
144 | }
145 | return errorWithValue(JenkinsUtil.unescape(fv.getMessage()), fv, value);
146 | }
147 |
148 | /**
149 | * True if the form control value was encoded by
150 | * {@link #errorWithValue(String, String)}.
151 | *
152 | * @param formControlValue form control value
153 | *
154 | * @return {@code true} if the form control value was encoded by
155 | * {@link #errorWithValue(String, String)}
156 | */
157 | public static boolean isError(String formControlValue) {
158 | return formControlValue != null && formControlValue.startsWith(ERROR_VALUE_PREFIX);
159 | }
160 |
161 | /**
162 | * Returns the value when the error occurred, or {@code null} if the form
163 | * control value was not encoded by {@link #errorWithValue(String, String)}.
164 | *
165 | * @param formControlValue form control value
166 | *
167 | * @return the value when the error occurred or {@code null} if the form
168 | * control value was not encoded by {@link #errorWithValue(String, String)}
169 | */
170 | public static String getErrorValue(String formControlValue) {
171 | return isError(formControlValue) ? formControlValue.substring(ERROR_VALUE_PREFIX.length()) : formControlValue;
172 | }
173 |
174 | /**
175 | * Extension of {@link JenkinsUtil#validateRequired} that always fails if
176 | * the form control value was encoded by
177 | * {@link #errorWithValue(String, String)}.
178 | *
179 | * @param formControlValue form control value
180 | * @return a {@link FormValidation} object created
181 | */
182 | public static FormValidation validateRequired(String formControlValue) {
183 | if (isError(formControlValue)) {
184 | return FormValidation.error(Messages.FormValidation_ValidateRequired());
185 | }
186 | return JenkinsUtil.validateRequired(formControlValue);
187 | }
188 |
189 | final HttpResponse response;
190 |
191 | private FormFillFailure(String message, Throwable cause, HttpResponse response) {
192 | super(message);
193 | this.response = response;
194 | }
195 |
196 | @Override
197 | public String toString() {
198 | return getClass().getSimpleName() + '[' + response.getClass().getSimpleName() + ": " + response + ']';
199 | }
200 |
201 | @Override
202 | public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
203 | response.generateResponse(req, rsp, node);
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/src/test/java/com/oracle/cloud/baremetal/jenkins/TestBaremetalCloudAgentTemplate.java:
--------------------------------------------------------------------------------
1 | package com.oracle.cloud.baremetal.jenkins;
2 |
3 | import java.util.Collection;
4 | import java.util.List;
5 | import java.util.Objects;
6 |
7 | import hudson.model.Node;
8 | import hudson.model.labels.LabelAtom;
9 |
10 |
11 | public class TestBaremetalCloudAgentTemplate extends BaremetalCloudAgentTemplate {
12 | public static class Builder {
13 | public boolean verificationStrategy;
14 | String description;
15 | String numExecutors;
16 | Node.Mode mode;
17 | String labelString;
18 | String idleTerminationMinutes;
19 | int templateId;
20 | String remoteFS;
21 | Boolean assignPublicIP;
22 | Boolean usePublicIP;
23 | String sshConnectTimeoutSeconds;
24 | String jenkinsAgentUser;
25 | String customJavaPath;
26 | String customJVMOpts;
27 | String initScript;
28 | Boolean exportJenkinsEnvVars;
29 | String startTimeoutSeconds;
30 | String initScriptTimeoutSeconds;
31 | String instanceCap;
32 | String compartmentId;
33 | String availableDomain;
34 | String vcnCompartmentId;
35 | String vcnId;
36 | String subnetCompartmentId;
37 | String subnetId;
38 | List nsgIds;
39 | String imageCompartmentId;
40 | String imageId;
41 | String shape;
42 | String sshCredentialsId;
43 | String ocpu;
44 | Boolean autoImageUpdate;
45 | Boolean stopOnIdle;
46 | List tags;
47 | String instanceNamePrefix;
48 | String memoryInGBs;
49 | Boolean doNotDisable;
50 | String retryTimeoutMins;
51 |
52 | public Builder description(String description) {
53 | this.description = description;
54 | return this;
55 | }
56 |
57 | public Builder numExecutors(String numExecutors) {
58 | this.numExecutors = numExecutors;
59 | return this;
60 | }
61 |
62 | public Builder numExecutors(int numExecutors) {
63 | return numExecutors(Integer.toString(numExecutors));
64 | }
65 |
66 | public Builder mode(Node.Mode mode) {
67 | this.mode = mode;
68 | return this;
69 | }
70 |
71 | public Builder labelString(String labelString) {
72 | this.labelString = labelString;
73 | return this;
74 | }
75 |
76 | public Builder idleTerminationMinutes(String idleTerminationMinutes) {
77 | this.idleTerminationMinutes = idleTerminationMinutes;
78 | return this;
79 | }
80 |
81 | public Builder verificationStrategy(Boolean verificationStrategy){
82 | this.verificationStrategy = verificationStrategy;
83 | return this;
84 | }
85 |
86 | public Builder templateId(int templateId) {
87 | this.templateId = templateId;
88 | return this;
89 | }
90 |
91 | public Builder compartmentId(String compartmentId) {
92 | this.compartmentId = compartmentId;
93 | return this;
94 | }
95 |
96 | public Builder availableDomain(String availableDomain) {
97 | this.availableDomain = availableDomain;
98 | return this;
99 | }
100 |
101 | public Builder vcnCompartmentId(String vcnCompartmentId) {
102 | this.vcnCompartmentId = vcnCompartmentId;
103 | return this;
104 | }
105 |
106 | public Builder vcnId(String vcnId) {
107 | this.vcnId = vcnId;
108 | return this;
109 | }
110 |
111 | public Builder subnetCompartmentId(String subnetCompartmentId) {
112 | this.subnetCompartmentId = subnetCompartmentId;
113 | return this;
114 | }
115 |
116 | public Builder subnetId(String subnetId) {
117 | this.subnetId = subnetId;
118 | return this;
119 | }
120 |
121 | public Builder assignPublicIP(Boolean assignPublicIP) {
122 | this.assignPublicIP = assignPublicIP;
123 | return this;
124 | }
125 |
126 | public Builder usePublicIP(Boolean usePublicIP) {
127 | this.usePublicIP = usePublicIP;
128 | return this;
129 | }
130 |
131 | public Builder imageCompartmentId(String imageCompartmentId) {
132 | this.imageCompartmentId = imageCompartmentId;
133 | return this;
134 | }
135 |
136 | public Builder imageId(String imageId) {
137 | this.imageId = imageId;
138 | return this;
139 | }
140 |
141 | public Builder shape(String shape) {
142 | this.shape = shape;
143 | return this;
144 | }
145 |
146 | public Builder initScriptTimeoutSeconds(String initScriptTimeoutSeconds) {
147 | this.initScriptTimeoutSeconds = initScriptTimeoutSeconds;
148 | return this;
149 | }
150 |
151 | public Builder retryTimeoutMins(String retryTimeoutMins) {
152 | this.retryTimeoutMins = retryTimeoutMins;
153 | return this;
154 | }
155 |
156 | public Builder instanceCap(String instanceCap) {
157 | this.instanceCap = instanceCap;
158 | return this;
159 | }
160 |
161 | public Builder ocpu(String ocpu) {
162 | this.ocpu = ocpu;
163 | return this;
164 | }
165 |
166 | public Builder autoImageUpdate(Boolean autoImageUpdate) {
167 | this.autoImageUpdate = autoImageUpdate;
168 | return this;
169 | }
170 |
171 | public Builder nsgIds(List nsgIds) {
172 | this.nsgIds = nsgIds;
173 | return this;
174 | }
175 |
176 | public Builder remoteFS(String remoteFS) {
177 | this.remoteFS = remoteFS;
178 | return this;
179 | }
180 |
181 | public Builder sshConnectTimeoutSeconds(String sshConnectTimeoutSeconds) {
182 | this.sshConnectTimeoutSeconds = sshConnectTimeoutSeconds;
183 | return this;
184 | }
185 |
186 | public Builder jenkinsAgentUser(String jenkinsAgentUser) {
187 | this.jenkinsAgentUser = jenkinsAgentUser;
188 | return this;
189 | }
190 |
191 | public Builder customJavaPath(String customJavaPath) {
192 | this.customJavaPath = customJavaPath;
193 | return this;
194 | }
195 |
196 | public Builder customJVMOpts(String customJVMOpts) {
197 | this.customJVMOpts = customJVMOpts;
198 | return this;
199 | }
200 |
201 | public Builder initScript(String initScript) {
202 | this.initScript = initScript;
203 | return this;
204 | }
205 |
206 | public Builder exportJenkinsEnvVars(Boolean exportJenkinsEnvVars) {
207 | this.exportJenkinsEnvVars = exportJenkinsEnvVars;
208 | return this;
209 | }
210 | public Builder startTimeoutSeconds(String startTimeoutSeconds) {
211 | this.startTimeoutSeconds = startTimeoutSeconds;
212 | return this;
213 | }
214 |
215 | public Builder stopOnIdle(Boolean stopOnIdle) {
216 | this.stopOnIdle = stopOnIdle;
217 | return this;
218 | }
219 |
220 | public Builder doNotDisable(Boolean doNotDisable) {
221 | this.doNotDisable = doNotDisable;
222 | return this;
223 | }
224 |
225 | public Builder tags(List tags) {
226 | this.tags = tags;
227 | return this;
228 | }
229 |
230 | public Builder instanceNamePrefix(String instanceNamePrefix) {
231 | this.instanceNamePrefix = instanceNamePrefix;
232 | return this;
233 | }
234 |
235 | public Builder memoryInGBs(String memoryInGBs) {
236 | this.memoryInGBs = memoryInGBs;
237 | return this;
238 | }
239 |
240 | public TestBaremetalCloudAgentTemplate build() {
241 | return new TestBaremetalCloudAgentTemplate(this);
242 | }
243 | }
244 |
245 | public TestBaremetalCloudAgentTemplate() {
246 | this(new Builder());
247 | }
248 |
249 | public TestBaremetalCloudAgentTemplate(Builder builder) {
250 | super(
251 | builder.compartmentId,
252 | builder.availableDomain,
253 | builder.vcnCompartmentId,
254 | builder.vcnId,
255 | builder.subnetCompartmentId,
256 | builder.subnetId,
257 | builder.nsgIds,
258 | builder.imageCompartmentId,
259 | builder.imageId,
260 | builder.shape,
261 | builder.sshCredentialsId,
262 | builder.description,
263 | builder.remoteFS,
264 | builder.assignPublicIP,
265 | builder.usePublicIP,
266 | builder.numExecutors,
267 | builder.mode,
268 | builder.labelString,
269 | builder.idleTerminationMinutes,
270 | builder.templateId,
271 | builder.jenkinsAgentUser,
272 | builder.customJavaPath,
273 | builder.customJVMOpts,
274 | builder.initScript,
275 | builder.exportJenkinsEnvVars,
276 | builder.sshConnectTimeoutSeconds,
277 | builder.verificationStrategy,
278 | builder.startTimeoutSeconds,
279 | builder.initScriptTimeoutSeconds,
280 | builder.instanceCap,
281 | builder.ocpu,
282 | builder.autoImageUpdate,
283 | builder.stopOnIdle,
284 | builder.tags,
285 | builder.instanceNamePrefix,
286 | builder.memoryInGBs,
287 | builder.doNotDisable,
288 | builder.retryTimeoutMins);
289 |
290 | }
291 |
292 | @Override
293 | Collection parseLabels(String strings) {
294 | return BaremetalCloudTestUtils.parseLabels(strings);
295 | }
296 |
297 | public static class TestDescriptor extends BaremetalCloudAgentTemplate.DescriptorImpl {
298 | public static class Builder {
299 | BaremetalCloud.DescriptorImpl cloudDescriptor;
300 |
301 | public Builder cloudDescriptor(BaremetalCloud.DescriptorImpl cloudDescriptor) {
302 | this.cloudDescriptor = cloudDescriptor;
303 | return this;
304 | }
305 |
306 | public TestDescriptor build() {
307 | return new TestDescriptor(this);
308 | }
309 | }
310 |
311 | private final BaremetalCloud.DescriptorImpl cloudDescriptor;
312 |
313 | public TestDescriptor(Builder builder) {
314 | this.cloudDescriptor = builder.cloudDescriptor;
315 | //this.pemDecoder = builder.pemDecoder;
316 | }
317 |
318 | @Override
319 | BaremetalCloud.DescriptorImpl getBaremetalCloudDescriptor() {
320 | return Objects.requireNonNull(cloudDescriptor, "cloudDescriptor");
321 | }
322 |
323 | }
324 | }
325 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | org.jenkins-ci.plugins
8 | plugin
9 | 4.79
10 |
11 |
12 | oracle-cloud-infrastructure-compute
13 | 1.0.19-SNAPSHOT
14 | hpi
15 |
16 |
17 | 17
18 | 2.46.0
19 | 2.462.3
20 | true
21 | 17
22 | 17
23 |
24 |
25 | Oracle Cloud Infrastructure Compute Plugin
26 | This plugin allows you to run dynamic slaves in the Oracle Cloud Infrastructure(OCI) Compute environment.
27 | https://github.com/jenkinsci/oracle-cloud-infrastructure-compute-plugin
28 |
29 |
30 |
31 | cheshi
32 | Chenghao Shi
33 | chenghao.shi@oracle.com
34 |
35 |
36 | kennedyjf
37 | Joe Kennedy
38 | joe.kennedy@oracle.com
39 |
40 |
41 | aandrey
42 | Andrey Andreyev
43 | andrey.adnreyev@oracle.com
44 |
45 |
46 | sindyarl
47 | Sindhu Sri Yarlagadda
48 | sindhu.sri.y.yarlagadda@oracle.com
49 |
50 |
51 | pcbajpai
52 | Prashant C Bajpai
53 | prashant.bajpai@oracle.com
54 |
55 |
56 | dmeirowi
57 | Diane Meirowitz
58 | diane.meirowitz@oracle.com
59 |
60 |
61 | jtorranc
62 | Jake Torrance
63 | jake.torrance@oracle.com
64 |
65 |
66 |
67 |
68 |
69 | The Universal Permissive License (UPL), Version 1.0
70 | http://www.oracle.com/technetwork/licenses/upl-license-2927578.html
71 | repo
72 |
73 |
74 | Apache License 2.0
75 | http://apache.org/licenses/LICENSE-2.0.html
76 | repo
77 |
78 |
79 |
80 |
81 | scm:git:https://orahub.oraclecorp.com/cloud-infra-qa/oracle-cloud-infrastructure-plugin
82 | scm:git:git@orahub.oraclecorp.com:cloud-infra-qa/oracle-cloud-infrastructure-plugin.git
83 | https://orahub.oraclecorp.com/cloud-infra-qa/oracle-cloud-infrastructure-plugin
84 |
85 |
86 |
87 | JIRS
88 | https://jira.oraclecorp.com/jira/browse/OPCTOOL
89 |
90 |
91 |
92 |
93 | repo.jenkins-ci.org
94 | https://repo.jenkins-ci.org/public/
95 |
96 |
97 |
98 |
99 | repo.jenkins-ci.org
100 | https://repo.jenkins-ci.org/public/
101 |
102 |
103 |
104 |
105 |
106 |
107 | io.jenkins.tools.bom
108 | bom-2.426.x
109 | 2907.vcb_35d6f2f7de
110 | import
111 | pom
112 |
113 |
114 |
115 |
116 |
117 | org.jenkins-ci.plugins
118 | credentials
119 | 1381.v2c3a_12074da_b_
120 |
121 |
122 | org.jenkins-ci.plugins
123 | ssh-credentials
124 |
125 |
126 | org.jenkins-ci
127 | trilead-ssh2
128 |
129 |
130 |
131 |
132 | org.jenkins-ci
133 | trilead-ssh2
134 | trilead-ssh2-build-217-jenkins-17
135 |
136 |
137 | org.jenkins-ci.plugins
138 | plain-credentials
139 | 183.va_de8f1dd5a_2b_
140 |
141 |
142 | org.jenkins-ci.plugins
143 | structs
144 | 338.v848422169819
145 |
146 |
147 | org.jenkins-ci.plugins
148 | credentials-binding
149 |
150 |
151 | org.jenkins-ci.plugins
152 | plain-credentials
153 |
154 |
155 | org.jenkins-ci.plugins
156 | structs
157 |
158 |
159 |
160 |
161 | org.jenkins-ci.plugins
162 | bouncycastle-api
163 | 2.30.1.78.1-248.ve27176eb_46cb_
164 |
165 |
166 | org.bouncycastle
167 | bcprov-jdk18on
168 |
169 |
170 |
171 |
172 | org.bouncycastle
173 | bcprov-jdk18on
174 | 1.78
175 |
176 |
177 | org.bouncycastle
178 | bcpkix-jdk18on
179 | 1.78
180 |
181 |
182 | org.codehaus.jettison
183 | jettison
184 | 1.5.4
185 |
186 |
187 | com.nimbusds
188 | nimbus-jose-jwt
189 | 9.37.2
190 |
191 |
192 | org.codehaus.jettison
193 | jettison
194 |
195 |
196 |
197 |
198 | com.oracle.oci.sdk
199 | oci-java-sdk-common
200 | ${oci-java-sdk.version}
201 |
202 |
203 | org.bouncycastle
204 | bcpkix-jdk15on
205 |
206 |
207 | org.bouncycastle
208 | bcprov-jdk15on
209 |
210 |
211 | org.glassfish.jersey.core
212 | *
213 |
214 |
215 | org.glassfish.jersey.media
216 | *
217 |
218 |
219 | org.glassfish.jersey.client
220 | *
221 |
222 |
223 | org.glassfish.jersey.connectors
224 | *
225 |
226 |
227 | org.glassfish.jersey.inject
228 | *
229 |
230 |
231 | com.fasterxml.jackson.core
232 | *
233 |
234 |
235 | jakarta.annotation
236 | *
237 |
238 |
239 | com.nimbusds
240 | nimbus-jose-jwt
241 |
242 |
243 |
244 |
245 | com.oracle.oci.sdk
246 | oci-java-sdk-core
247 | ${oci-java-sdk.version}
248 |
249 |
250 | com.oracle.oci.sdk
251 | oci-java-sdk-identity
252 | ${oci-java-sdk.version}
253 |
254 |
255 | io.jenkins.plugins
256 | javax-activation-api
257 | 1.2.0-7
258 |
259 |
260 | io.jenkins.plugins
261 | jersey2-api
262 | 2.44-151.v6df377fff741
263 |
264 |
265 | org.codehaus.jettison
266 | jettison
267 |
268 |
269 |
270 |
271 | org.jenkins-ci.plugins
272 | jackson2-api
273 | 2.17.0-379.v02de8ec9f64c
274 |
275 |
276 | io.jenkins.plugins
277 | jakarta-activation-api
278 |
279 |
280 | org.jmock
281 | jmock-junit4
282 | 2.13.1
283 | test
284 |
285 |
286 | commons-codec
287 | commons-codec
288 |
289 |
290 | org.apache.commons
291 | commons-lang3
292 | 3.14.0
293 |
294 |
295 | com.google.guava
296 | guava
297 |
298 |
299 | org.slf4j
300 | slf4j-api
301 |
302 |
303 | commons-io
304 | commons-io
305 | 2.15.1
306 |
307 |
308 |
309 |
310 |
311 |
312 | ${project.basedir}
313 |
314 | LICENSE.txt
315 | NOTICE.txt
316 | THIRD_PARTY_LICENSES.txt
317 |
318 |
319 |
320 | ${project.basedir}/src/main/resources
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 | org.apache.maven.plugins
330 | maven-project-info-reports-plugin
331 |
332 |
333 |
334 | dependencies
335 | scm
336 | issue-tracking
337 | license
338 |
339 |
340 |
341 |
342 | false
343 | false
344 |
345 |
346 |
347 | org.apache.maven.plugins
348 | maven-javadoc-plugin
349 |
350 |
351 |
352 | javadoc
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2 |
3 | This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at
4 | https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0.
5 | You may choose either license.
6 |
7 | ____________________________
8 |
9 | The Universal Permissive License (UPL), Version 1.0
10 | Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
11 |
12 | Subject to the condition set forth below, permission is hereby granted to any person obtaining a copy of this software,
13 | associated documentation and/or data (collectively the "Software"), free of charge and under any and all copyright
14 | rights in the Software, and any and all patent rights owned or freely licensable by each licensor hereunder covering
15 | either (i) the unmodified Software as contributed to or provided by such licensor,
16 | or (ii) the Larger Works (as defined below), to deal in both
17 |
18 | (a) the Software, and
19 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if one is included with the Software (each a
20 | "Larger Work" to which the Software is contributed by such licensors),
21 |
22 | without restriction, including without limitation the rights to copy, create derivative works of, display, perform,
23 | and distribute the Software and make, use, sell, offer for sale, import, export, have made, and have sold the Software
24 | and the Larger Work(s), and to sublicense the foregoing rights on either these or other terms.
25 |
26 | This license is subject to the following condition:
27 |
28 | The above copyright notice and either this complete permission notice or at a minimum a reference to the UPL must be
29 | included in all copies or substantial portions of the Software.
30 |
31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
32 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
33 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
34 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 |
36 | The Apache Software License, Version 2.0
37 | Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
38 |
39 | Licensed under the Apache License, Version 2.0 (the "License"); You may not use this product except in compliance with
40 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. A copy of the license
41 | is also reproduced below. Unless required by applicable law or agreed to in writing, software distributed under the
42 | License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
43 | See the License for the specific language governing permissions and limitations under the License.
44 |
45 | Apache License
46 |
47 | Version 2.0, January 2004
48 |
49 | http://www.apache.org/licenses/
50 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
51 | 1. Definitions.
52 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through
53 | 9 of this document.
54 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
55 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or
56 | are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct
57 | or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership
58 | of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
59 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
60 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code,
61 | documentation source, and configuration files.
62 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including
63 | but not limited to compiled object code, generated documentation, and conversions to other media types.
64 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as
65 | indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
66 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work
67 | and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an
68 | original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain
69 | separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
70 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or
71 | additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the
72 | Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner.
73 | For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to
74 | the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code
75 | control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of
76 | discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in
77 | writing by the copyright owner as "Not a Contribution."
78 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received
79 | by Licensor and subsequently incorporated within the Work.
80 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to
81 | You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
82 | Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works
83 | in Source or Object form.
84 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to
85 | You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section)
86 | patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license
87 | applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s)
88 | alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted.
89 | If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging
90 | that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement,
91 | then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation
92 | is filed.
93 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium,
94 | with or without modifications, and in Source or Object form, provided that You meet the following conditions:
95 | You must give any other recipients of the Work or Derivative Works a copy of this License; and
96 | You must cause any modified files to carry prominent notices stating that You changed the files; and
97 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and
98 | attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the
99 | Derivative Works; and
100 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute
101 | must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that
102 | do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file
103 | distributed as part of the Derivative Works; within the Source form or documentation, if provided along with
104 | the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices
105 | normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License.
106 | You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the
107 | NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
108 |
109 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and
110 | conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole,
111 | provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
112 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for
113 | inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any
114 | additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any
115 | separate license agreement you may have executed with Licensor regarding such Contributions.
116 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product
117 | names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and
118 | reproducing the content of the NOTICE file.
119 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and
120 | each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
121 | express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
122 | MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of
123 | using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
124 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or
125 | otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing,
126 | shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential
127 | damages of any character arising as a result of this License or out of the use or inability to use the Work (including
128 | but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other
129 | commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
130 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose
131 | to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights
132 | consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your
133 | sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each
134 | Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your
135 | accepting any such warranty or additional liability.
136 | END OF TERMS AND CONDITIONS
137 |
138 | APPENDIX: How to apply the Apache License to your work.
139 |
140 | To apply the Apache License to your work, attach the following
141 | boilerplate notice, with the fields enclosed by brackets "[]"
142 | replaced with your own identifying information. (Don't include
143 | the brackets!) The text should be enclosed in the appropriate
144 | comment syntax for the file format. We also recommend that a
145 | file or class name and description of purpose be included on the
146 | same "printed page" as the copyright notice for easier
147 | identification within third-party archives.
148 |
149 | Copyright [yyyy] [name of copyright owner]
150 |
151 | Licensed under the Apache License, Version 2.0 (the "License");
152 | you may not use this file except in compliance with the License.
153 | You may obtain a copy of the License at
154 |
155 | http://www.apache.org/licenses/LICENSE-2.0
156 |
157 | Unless required by applicable law or agreed to in writing, software
158 | distributed under the License is distributed on an "AS IS" BASIS,
159 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
160 | See the License for the specific language governing permissions and
161 | limitations under the License.
--------------------------------------------------------------------------------