├── 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 |
2 | See the 3 | Network Security Groups 4 | documentation for additional information. 5 |
-------------------------------------------------------------------------------- /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 |
2 | The OCI API private key. 3 | See the 4 | How to Generate an API Signing Key 5 | documentation for additional information. 6 |
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 | 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 |
2 | Fingerprint for the key pair being used. 3 | See the 4 | How to Get the Key's Fingerprint 5 | documentation for additional information. 6 |
7 | -------------------------------------------------------------------------------- /src/main/resources/com/oracle/cloud/baremetal/jenkins/credentials/BaremetalCloudCredentialsImpl/help-passphrase.html: -------------------------------------------------------------------------------- 1 |
2 | PassPhrase for the key pair being used. 3 | See the 4 | How to Generate an API Signing Key 5 | documentation for additional information. 6 |
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 | 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 | 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 |
12 | This plugin allows you to run dynamic slaves in the Oracle Cloud Infrastructure(OCI) Compute environment. 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 | -------------------------------------------------------------------------------- /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 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 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("\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("", ""); 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 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. --------------------------------------------------------------------------------