diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fef1cd3..01f2d55e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ - [SECURITY] prevent directly entered private key to be exported in plain test - fix and improve generated documentation - use BulkChange to avoid repeated calls to save() -- list available attributes when unknown found in yaml to help diagnose mistakes +- list available attributes when unknown found in YAML to help diagnose mistakes - log a warning when descriptor with unexpected design is detected ## 1.1 diff --git a/COMPATIBILITY.adoc b/COMPATIBILITY.adoc index 5aa78ecc..5e6756e7 100644 --- a/COMPATIBILITY.adoc +++ b/COMPATIBILITY.adoc @@ -1,10 +1,10 @@ -= Plugin Compatibility with Configuration-as-code plugin += Plugin Compatibility with configuration-as-code plugin This document captures the ongoing status of plugins known to be compatible or incompatible. Because of how the tool was designed, plugins _should_ be expected to work out of the box if they followed the main patterns in place. -There is a list of already known issues in the Jenkins issue tracker, tracked through using the label `jcasc-incompatible` (link:https://issues.jenkins-ci.org/secure/Dashboard.jspa?selectPageId=17346[see dashboard]). +There is a list of already known issues in the Jenkins issue tracker, tracked through using the label `jcasc-incompatible` (link:https://issues.jenkins.io/secure/Dashboard.jspa?selectPageId=17346[see dashboard]). This document is an *ongoing* community effort. Hence a missing plugin here and has no existing issue filed in JIRA (see above) does not _necessarily_ mean it won't work. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fd36e17a..cc6b9718 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,6 +19,6 @@ Whenever you report a problem please provide information about: 1. Create a github issue for your feature/problem you want to solve 2. Implement solution on a branch in your fork 3. Make sure to include issue number in commit message, and make the message speak for itself -4. Once you're done create Pull Request and ask at least one of the maintainers for review (@ndeloof, @ewelinawilkosz) +4. Once you're done create a pull request and ask at least one of the maintainers for review (@ndeloof, @ewelinawilkosz) !! Never push directly to this repository diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index 3597e71d..fad1f88b 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -1,70 +1,71 @@ -# Jenkins Configuration as Code : implementation details +# Implementation Details -Input configuration file uses a YAML hierarchical data structure. -every node of this data structure is passed to a `Configurator` responsible -to apply the adequate configuration on Jenkins live instance. +The configuration file uses a YAML hierarchical data structure. +Every node of this data structure is passed to a `Configurator` responsible +to apply the adequate configuration on a Jenkins live instance. ## Configurator A [`Configurator`](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/plugin/src/main/java/io/jenkins/plugins/casc/Configurator.java) -is managing a specific Jenkins Component, and as such knows -about data this component exposes to end-user for configuration. +is managing a specific Jenkins component, and as such knows +about data this component exposes to end users for configuration. + It has: -* a `name` to match a YAML entry, +* a `name` to match a YAML entry * a `target` component type * a `describe` method to document the attributes the target component exposes to configuration * a `configure` method to configure the target component -From a yaml node with associated `Configurator`, configuration-as-code will handle every -child node in YAML structure based on current node's `Attribute`s, as described by the `Configurator`. +From a YAML node with an associated `Configurator`, configuration-as-code will handle every +child node in the YAML structure based on the current node's `Attribute`s, as described by the `Configurator`. ## Root Configurator selection Root elements are identified by YAML entry name, and a matching -[`RootElementConfigurator`](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/plugin/src/main/java/io.jenkins/plugins/casc/RootElementConfigurator.java) is selected. +[`RootElementConfigurator`](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/plugin/src/main/java/io/jenkins/plugins/casc/RootElementConfigurator.java) is selected. `RootElementConfigurator` is a special interface used to identify a `Configurator` which manages a top level -configuration element in the yaml document. +configuration element in the YAML document. -configuration-as-code do provide a custom `RootElementConfigurator` for `jenkins` root entry in yaml document, +configuration-as-code provides a custom `RootElementConfigurator` for the `jenkins` root entry in the YAML document, as well as generic `RootElementConfigurator`s to model [global configuration categories](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/jenkins/model/GlobalConfigurationCategory.java). ### Attributes -Configurator do document the target component by implementing `describe` method. This one do returl a set -of [`Attribute`](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/plugin/src/main/java/io.jenkins/plugins/casc/Attribute.java)s +`Configurator` documents the target component by implementing the `describe` method. This method returns a set +of [`Attribute`](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/plugin/src/main/java/io/jenkins/plugins/casc/Attribute.java)s to document both name _AND_ type for a configurable attribute. -We don't want to expose the whole Jenkins Java API to configuration-as-code. Many components do define setter -methods for technical reasons or to support backward compatibility. So configuration-as-code do exclude : +We don't want to expose the whole Jenkins Java API to configuration-as-code. Many components define setter +methods for technical reasons or to support backward compatibility. So configuration-as-code excludes: + * setter methods marked as `@Deprecated` * setter methods marked as `@Restricted` -Attribute also is responsible to document the target component type. We use Java reflexion to extract this -information from Java API, including parameterized types. +`Attribute` also is responsible to document the target component type. We use Java reflection to extract this +information from the Java API, including parameterized types. -As a resume, a component which exposes a method like : +As a resume, a component which exposes a method like: ```java -public void setFoo(List foos) { ... } - +public void setFoo(List foos) { ... } ``` -will be detected as Attribute named `foo` with target type `Foo` with multiple values expected. +will be detected as attribute named `foo` with target type `Foo` with multiple values expected. ### Configuration -Configurator also has to implement the `configure` method to process yaml content and instantiate or configure -target component. The actual mechanism depends on the target. Configuration-as-Code do provide generic +Configurator also has to implement the `configure` method to process YAML content and instantiate or configure +the target component. The actual mechanism depends on the target. Configuration-as-Code provides generic mechanisms which cover most Jenkins components. This generic mechanism assumes Jenkins core components and plugins do follow some conventions, but custom "glue-code" can also be provided as a (temporary) workaround, or to expose a configuration model -which doesn't reflect the internal data model, but better matches the end-user experience. +which doesn't reflect the internal data model, but better matches the end user experience. -A typical sample for this scenario is credentials-plugin. -[CredentialsRootConfigurator](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/plugin/src/main/java/io.jenkins/plugins/casc/credentials/CredentialsRootConfigurator.java) -do expose a simplified configuration API for `system` credentials store, which is hardly configurable -using the other general purpose configuration-as-code component due to internal design of this plugin. +A typical sample for this scenario is the [credentials](https://plugins.jenkins.io/credentials) plugin. +[CredentialsRootConfigurator](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/support/src/main/java/io/jenkins/plugins/casc/support/credentials/CredentialsRootConfigurator.java) +exposes a simplified configuration API for the `system` credentials store, which is hardly configurable +using the other general purpose configuration-as-code component due to the internal design of this plugin. ## General purpose configurators @@ -74,27 +75,27 @@ As we want to reflect web UI user experience, relying on the same configuration UI is a natural approach. `org.jenkinsci.plugins.casc.impl.configurators.DataBoundConfigurator` can configure arbitrary -jenkins component to rely on `DataBoundConstructor` -and `DataBoundSetter`s for UI data-binding. It uses same attributes names as +Jenkins components to rely on `DataBoundConstructor` +and `DataBoundSetter`s for UI data-binding. It uses the same attribute names as the web UI, which are expected to be human friendly. -When, for some technical or legacy reasons, technical attribute name isn't user friendly, we also support -`@Symbol` annotation on setter to offer a better user-experience. +When, for technical or legacy reasons, the technical attribute name isn't user friendly, we also support +`@Symbol` annotation on setters to offer a better user experience. ### Descriptors `org.jenkinsci.plugins.casc.DescriptorRootElementConfigurator` can configure global configuration for Descriptors, to mimic the `global.jelly` UI exposed -to end user on the web UI. +to end users on the web UI. -Jenkins has hundreds Descriptors, most of them for internal technical reasons, -so only the ones to have a `global` view are accessible from configuration-as-code. +Jenkins has hundreds of Descriptors, most of them for internal technical reasons, +so only the ones that have a `global` view are accessible from configuration-as-code. For Descriptors to work well with configuration-as-code, they need to follow -[some design best practices](PLUGINS.md) in terms of data binding. This is not such a common thing, +[some design best practices](docs/PLUGINS.md) in terms of data binding. This is not such a common thing, so we expect this will require some evangelism on plugin developers. -As a short terms workaround, a custom `Configurator` glue-code implementation can be implemented. +As short term workaround, a custom `Configurator` glue-code implementation can be implemented. ## Initial secrets diff --git a/README.md b/README.md index 256989ba..9dac723b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Experienced Jenkins users rely on groovy init scripts to customize Jenkins and e scripts directly invoke Jenkins API and as such can do everything (at your own risk). But they also require you know Jenkins internals, and are confident in writing groovy scripts on top of Jenkins API. -Configuration-as-Code plugin has been designed as an _**opinionated**_ way to configure Jenkins based on +The configuration-as-code plugin has been designed as an _**opinionated**_ way to configure Jenkins based on human-readable declarative configuration files. Writing such a file should be feasible without being a Jenkins expert, just translating into _code_ a configuration process one is used to executing in the web UI. @@ -45,7 +45,7 @@ so end-users have full guidance in using this tool-set and do not have to search ## Getting Started -First, start a Jenkins instance with JCasC [plugin](https://plugins.jenkins.io/configuration-as-code) installed. +First, start a Jenkins instance with the [configuration-as-code](https://plugins.jenkins.io/configuration-as-code) plugin installed. - Those running Jenkins as a [Docker container](https://github.com/jenkinsci/docker) (and maybe also [pre-installing plugins](https://github.com/jenkinsci/docker#preinstalling-plugins)), do include [configuration-as-code](https://plugins.jenkins.io/configuration-as-code) plugin and optionally the [configuration-as-code-support](https://plugins.jenkins.io/configuration-as-code-support) plugin if you use one of the credentials plugins or the job-dsl (see link). @@ -141,12 +141,12 @@ INFO: Jenkins is fully up and running ## Initial Configuration When configuring the first Jenkins instance, browse the examples shown in the [demos](demos) -directory of this repository. If you have a plugin that do not have an example, consult the reference +directory of this repository. If you have a plugin that does not have an example, consult the reference help document. Click the `Documentation` link at the bottom of the Configuration as Code page. ![Reference Page](images/reference.png) -If you might wish to configure a specific plugin, search the page for the name of the plugin. The page will +If you want to configure a specific plugin, search the page for the name of the plugin. The page will show you which root element belongs to the configuration. Most installed plugins belong under the `unclassified` root element. @@ -163,7 +163,7 @@ jenkins: nodes: - permanent: - name: "static-slave" + name: "static-agent" remoteFS: "/home/jenkins" launcher: jnlp: @@ -201,18 +201,18 @@ Also see [demos](demos) folder with various samples. The configuration file format depends on the version of jenkins-core and installed plugins. Documentation is generated from a live instance, as well as a JSON-schema you can use to validate configuration file -with your favourite yaml tools. +with your favourite YAML tools. ## Handling Secrets Currently, you can provide initial secrets to Configuration-as-Code that all rely on -substitution of strings in configuration. For example, ``Jenkins: `${some_var}` ``. Default variable substitution +substitution of strings in the configuration. For example, ``Jenkins: `${some_var}` ``. Default variable substitution using the `:-` operator from `bash` is also available. For example, `key: ${VALUE:-defaultvalue}` will evaluate to `defaultvalue` if `$VALUE` is unset. To escape a string from secret interpolation, put `^` in front of the value. For example, `Jenkins: ^${some_var}` will produce the literal `Jenkins: ${some_var}`. We can provide these initial secrets in the following ways: - Using environment variables. -- Using docker-secrets, where files on path `/run/secrets/${KEY}` will be replaced by `${KEY}` in configuration. The base folder `/run/secrets` can be overriden by setting the environment variable `SECRETS`. So this can be used as a file based secret, and not just docker secrets. +- Using docker-secrets, where files on path `/run/secrets/${KEY}` will be replaced by `${KEY}` in the configuration. The base folder `/run/secrets` can be overridden by setting the environment variable `SECRETS`. So this can be used as a file based secret, and not just docker secrets. - Using Kubernetes secrets, logic is the same as for docker-secrets. The secret needs to be mounted as a file to `/run/secrets/`, and then the filename can be used as the KEY. For example: ```yaml @@ -242,7 +242,7 @@ Prerequisites: - The environment variable `CASC_VAULT_PW` must be present, if token is not used and appRole/Secret is not used. (Vault password.) - The environment variable `CASC_VAULT_USER` must be present, if token is not used and appRole/Secret is not used. (Vault username.) - The environment variable `CASC_VAULT_APPROLE` must be present, if token is not used and U/P not used. (Vault AppRole ID.) -- The environment variable `CASC_VAULT_APPROLE_SECRET` must be present, it token is not used and U/P not used. (Value AppRole Secret ID.) +- The environment variable `CASC_VAULT_APPROLE_SECRET` must be present, it token is not used and U/P not used. (Vault AppRole Secret ID.) - The environment variable `CASC_VAULT_TOKEN` must be present, if U/P is not used. (Vault token.) - The environment variable `CASC_VAULT_PATHS` must be present. (Comma separated vault key paths. For example, `secret/jenkins,secret/admin`.) - The environment variable `CASC_VAULT_URL` must be present. (Vault url, including port number.) @@ -253,7 +253,7 @@ Prerequisites: If the environment variables `CASC_VAULT_URL` and `CASC_VAULT_PATHS` are present, Configuration-as-Code will try to gather initial secrets from Vault. However for it to work properly there is a need for authentication by either the combination of `CASC_VAULT_USER` and `CASC_VAULT_PW`, a `CASC_VAULT_TOKEN`, or the combination of `CASC_VAULT_APPROLE` and `CASC_VAULT_APPROLE_SECRET`. The authenticated user must have at least read access. -You can also provide a `CASC_VAULT_FILE` environment variable where you load the secrets from file. +You can also provide a `CASC_VAULT_FILE` environment variable where you load the secrets from a file. File should be in a Java Properties format @@ -304,7 +304,7 @@ We currently do support plugin installation but it will remain in `beta` for the we recommend that you package your plugins with your Jenkins distribution as plugin installation often requires a restart and can cause problems with plugin dependencies. So if you want to try it, you can. -Current implementation do require a restart if you add a plugin. +The current implementation does require a restart if you add a plugin. Example: (Requires Configuration as Code Plugin version > 0.7-alpha) @@ -317,10 +317,10 @@ plugins: ## Supported Plugins -Most plugins should be supported out-of-the-box, or maybe require some minimal changes. See this [dashboard](https://issues.jenkins-ci.org/secure/Dashboard.jspa?selectPageId=17346) for known compatibility issues. +Most plugins should be supported out-of-the-box, or maybe require some minimal changes. See this [dashboard](https://issues.jenkins.io/secure/Dashboard.jspa?selectPageId=17346) for known compatibility issues. ## Jenkins Enhancement Proposal -As Configuration-as-code is demonstrated to be a highly requested topic in Jenkins community, we have published +As configuration-as-code is demonstrated to be a highly requested topic in Jenkins community, we have published [JEP 201](https://github.com/jenkinsci/jep/tree/master/jep/201) as proposal to make this a standard component of the Jenkins project. The proposal was accepted. :tada: diff --git a/demos/artifactory/README.md b/demos/artifactory/README.md index a3448203..13f0dde6 100644 --- a/demos/artifactory/README.md +++ b/demos/artifactory/README.md @@ -22,6 +22,7 @@ unclassified: ``` ## implementation note + Currently setting credentials causes ERROR & `Enable Push to Bintray` is not supported (always enabled). see [https://www.jfrog.com/jira/browse/HAP-1018] diff --git a/demos/config-file-provider/README.md b/demos/config-file-provider/README.md index a8114b58..4e7f80a5 100644 --- a/demos/config-file-provider/README.md +++ b/demos/config-file-provider/README.md @@ -48,5 +48,3 @@ unclassified: ``` - - diff --git a/demos/docker/README.md b/demos/docker/README.md index a61a8b43..8f0c744c 100644 --- a/demos/docker/README.md +++ b/demos/docker/README.md @@ -32,4 +32,3 @@ jenkins: Jenkins singleton doesn't offer any `setClouds` method. So here we rely on a pseudo-property implemented by a dedicated `Attribute` to add the configured clouds to `Jenkins.clouds`. The current implementation only adds the configured cloud if it doesn't exists yet. - diff --git a/demos/ec2/README.md b/demos/ec2/README.md index 2158a073..e853e663 100644 --- a/demos/ec2/README.md +++ b/demos/ec2/README.md @@ -1,4 +1,4 @@ -# configure Amazon EC2 Plguin +# configure Amazon EC2 Plugin https://wiki.jenkins.io/display/JENKINS/Amazon+EC2+Plugin diff --git a/demos/git/README.md b/demos/git/README.md index e5961132..c83a8367 100644 --- a/demos/git/README.md +++ b/demos/git/README.md @@ -14,12 +14,10 @@ tool: ## implementation note -Here we relies on `hudson.tools.ToolDescriptor.setInstallations`, so same applies to all ToolInstallations. -Unfortunately java reflection make it hack-ish to detect the parameter type of this method from derived concrete +Here we rely on `hudson.tools.ToolDescriptor.setInstallations`, so same applies to all ToolInstallations. +Unfortunately Java reflection makes it hack-ish to detect the parameter type of this method from derived concrete class, so maybe there's some corner case we will need to polish this logic. -Also, Yaml lists are converted into `ArrayLists` but `setInstallations(T ... installation)` varags method require -and array - blame java to not just accept any `Iterable` - so we need to detect this scenario and do the type +Also, YAML lists are converted into `ArrayLists` but `setInstallations(T ... installation)` varags method require +an array - blame Java to not just accept any `Iterable` - so we need to detect this scenario and do the type conversion. - - diff --git a/demos/gitlab/README.md b/demos/gitlab/README.md index 617f53fd..a63cf942 100644 --- a/demos/gitlab/README.md +++ b/demos/gitlab/README.md @@ -25,5 +25,3 @@ unclassified: readTimeout: 10 url: "https://gitlab.com/" ``` - - diff --git a/demos/jenkins/README.md b/demos/jenkins/README.md index 969f85d2..3e7c9469 100644 --- a/demos/jenkins/README.md +++ b/demos/jenkins/README.md @@ -4,8 +4,7 @@ Basic Jenkins configuration under `Configure System`, which is not a part of any Many of the plugins are actually configured in the same section, but to configure them you'll put their configuration under `unclassified` root element - details in plugin's specific subfolders. -[jenkins.yaml](jenkins.yaml) file is an example of configuration file with Jenkins and a number of plugins configured. - +[jenkins.yaml](jenkins.yaml) file is an example of a configuration file with Jenkins and a number of plugins configured. ## sample configuration @@ -18,7 +17,8 @@ jenkins: ``` ### Multiline system message -There are (too) many ways to write multi-line strings in yaml, but one of the most readable solutions + +There are (too) many ways to write multi-line strings in YAML, but one of the most readable solutions is to use the following syntax, that doesn't need escaped newlines and other shenanigans: ```yaml @@ -30,10 +30,11 @@ jenkins: Config is now mostly handled by 'Jenkins Configuration as Code Plugin' (JCasC). JCasC config can be found in the jenkins.yaml file in the $JENKINS_HOME/casc/ folder. - some settings are still injected from init.groovy.d scripts, + Some settings are still injected from init.groovy.d scripts, but these settings will be ported over to JCasC as support becomes available. - numExecutors: 1 # This is just a random example entry to show that there is no "end token" for the multiline string apart from un-indent to the next yaml property. + numExecutors: 1 # This is just a random example entry to show that there is no "end token" for the multiline string apart from un-indent to the next YAML property. ``` # implementation note -Example above is only a subset of commonly used settings, full list available in generated documentation + +The example above is only a subset of commonly used settings. The full list is available in the generated documentation. diff --git a/demos/jobs/README.md b/demos/jobs/README.md index 5a941af3..1fdbcb2c 100644 --- a/demos/jobs/README.md +++ b/demos/jobs/README.md @@ -4,20 +4,20 @@ As explained in [seed-jobs.md](../../docs/seed-jobs.md), `jobs` declaration is u For now, it is using the [job-dsl-plugin](https://wiki.jenkins.io/display/JENKINS/Job+DSL+Plugin) so this plugin needs to be installed on your Jenkins instance for this sample to work. -Job-DSL plugin uses groovy syntax for it's job configuration DSL, so a mix of yaml and groovy must be used within the +The Job DSL plugin uses groovy syntax for its job configuration DSL, so a mix of YAML and groovy must be used within the configuration-as-code file. ## sample configurations -[bitbucket.yaml](bitbucket.yaml) file is an example of configuration file with Jenkins and an Organization Folder Job Type with automatic branch discovering in Bitbucket. It requires [Branch API plugin](https://github.com/jenkinsci/branch-api-plugin) and [Bitbucket Branch Source plugin](https://github.com/jenkinsci/bitbucket-branch-source-plugin) to be able to run this demo. `$BITBUCKET_URL` is a system environment variable that needs to be defined before Jenkins is started. +[bitbucket.yaml](bitbucket.yaml) file is an example of a configuration file with Jenkins and an Organization Folder Job Type with automatic branch discovering in Bitbucket. It requires [Branch API plugin](https://github.com/jenkinsci/branch-api-plugin) and [Bitbucket Branch Source plugin](https://github.com/jenkinsci/bitbucket-branch-source-plugin) to be able to run this demo. `$BITBUCKET_URL` is a system environment variable that needs to be defined before Jenkins is started. -[pipeline.yaml](pipeline.yaml) file is an example of configuring a folder and a descrative pipeline job within that folder. +[pipeline.yaml](pipeline.yaml) file is an example of configuring a folder and a declarative pipeline job within that folder. [multibranch-github.yaml](multibranch-github.yaml) file is an example of a multibranch pipeline job configured with GitHub as branch source, an orphaned item strategy and periodic scan triggers of 5 mins. ## implementation note -- The main issue in `jobs` declaration for now concerns the difference in 'Traits' declaration due to https://issues.jenkins-ci.org/browse/JENKINS-45504. -When is is resolved, the workaround using `configure` part will no longer be needed and all traits will be declared under the organizations section. +- The main issue with the `jobs` declaration for now is the difference in the 'Traits' declaration due to https://issues.jenkins.io/browse/JENKINS-45504. +When is is resolved, the workaround using the `configure` part will no longer be needed and all traits will be declared under the organizations section. -- JobDSL only allow 'periodic(int min)' for configuring trigger for now. So to configure "1 day" for example, we need to use the `configure` workaround as shown in [bitbucket.yaml](bitbucket.yaml#L68) +- Job DSL only allows 'periodic(int min)' for configuring trigger for now. So to configure "1 day" for example, we need to use the `configure` workaround as shown in [bitbucket.yaml](bitbucket.yaml#L68) diff --git a/demos/jobs/bitbucket.yaml b/demos/jobs/bitbucket.yaml index 614e3cb2..10f491bc 100644 --- a/demos/jobs/bitbucket.yaml +++ b/demos/jobs/bitbucket.yaml @@ -37,7 +37,7 @@ jobs: // "Traits" ("Behaviours" in the GUI) that are NOT "declarative-compatible" // For some 'traits, we need to configure this stuff by hand until JobDSL handles it - // https://issues.jenkins-ci.org/browse/JENKINS-45504 + // https://issues.jenkins.io/browse/JENKINS-45504 configure { node -> def traits = node / navigators / 'com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMNavigator' / traits traits << 'com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait' { diff --git a/demos/keycloak/README.md b/demos/keycloak/README.md index 992ab155..c3b302cc 100644 --- a/demos/keycloak/README.md +++ b/demos/keycloak/README.md @@ -18,4 +18,3 @@ unclassified: "confidential-port": 0 } ``` - diff --git a/demos/kubernetes-helm/README.md b/demos/kubernetes-helm/README.md index 341a399b..12a52aad 100644 --- a/demos/kubernetes-helm/README.md +++ b/demos/kubernetes-helm/README.md @@ -2,10 +2,10 @@ ## Preparation -Jenkins can be installed in Kubernetes using [`helm`](https://github.com/helm/helm). -The latest stable helm chart can be found [`here`](https://github.com/helm/charts/tree/master/stable/jenkins). +Jenkins can be installed in Kubernetes using [helm](https://github.com/helm/helm). +The latest stable helm chart can be found [here](https://github.com/helm/charts/tree/master/stable/jenkins). -Now grab a copy of the helm chart [`values file`](https://github.com/helm/charts/blob/master/stable/jenkins/values.yaml) and adjust the Master part a little bit: +Now grab a copy of the helm chart [values file](https://github.com/helm/charts/blob/master/stable/jenkins/values.yaml) and adjust the Master part a little bit: ```yaml Master: @@ -23,9 +23,9 @@ Master: # Below is the implementation of Jenkins Configuration as Code. Add a key under ConfigScripts for each configuration area, # where each corresponds to a plugin or section of the UI. Each key (prior to | character) is just a label, and can be any value. # Keys are only used to give the section a meaningful name. The only restriction is they may only contain RFC 1123 \ DNS label - # characters: lowercase letters, numbers, and hyphens. The keys become the name of a configuration yaml file on the master in + # characters: lowercase letters, numbers, and hyphens. The keys become the name of a configuration YAML file on the master in # /var/jenkins_home/casc_configs (by default) and will be processed by the Configuration as Code Plugin. The lines after each | - # become the content of the configuration yaml file. The first line after this is a JCasC root element, eg jenkins, credentials, + # become the content of the configuration YAML file. The first line after this is a JCasC root element, eg jenkins, credentials, # etc. Best reference is https:///configuration-as-code/reference. The example below creates a welcome message: JCasC: enabled: true @@ -77,7 +77,7 @@ Master: value: /var/jenkins_home/casc_configs/..data/jenkins.yaml # `SECRETS` is used to override the `/run/secrets` path, # which is useful for setting credentials. - # But it needs to be used in conjuction with Master.SecretsFilesSecret + # But it needs to be used in conjunction with Master.SecretsFilesSecret # - name: SECRETS # value: /usr/share/jenkins/ref/secrets @@ -102,4 +102,4 @@ Once Helm finishes deploying the chart, connect to your Jenkins server in your b ## Misc -Check the [`Configuration as Code`](https://github.com/helm/charts/tree/master/stable/jenkins#configuration-as-code) section in the Jenkins Helm chart repo for more information. +Check the [Configuration as Code](https://github.com/helm/charts/tree/master/stable/jenkins#configuration-as-code) section in the Jenkins Helm chart repo for more information. diff --git a/demos/kubernetes-helm/values.yaml b/demos/kubernetes-helm/values.yaml index a0e9ba30..69363281 100644 --- a/demos/kubernetes-helm/values.yaml +++ b/demos/kubernetes-helm/values.yaml @@ -16,7 +16,7 @@ Master: OwnSshKey: false # If CasC auto-reload is enabled, an SSH (RSA) keypair is needed. Can either provide your own, or leave unconfigured\false to allow a random key to be auto-generated. # If you choose to use your own, you must upload your decrypted RSA private key (not the public key above) to a Kubernetes secret using the following command: - # kubectl -n create secret generic --dry-run --from-file=jenkins-admin-private-key=~/.ssh/id_rsa -o yaml |kubectl -n apply -f - + # kubectl -n create secret generic --dry-run --from-file=jenkins-admin-private-key=~/.ssh/id_rsa -o YAML |kubectl -n apply -f - # Replace ~/.ssh/id_rsa in the above command with the path to your private key file and the and placeholders to suit. resources: requests: @@ -29,9 +29,9 @@ Master: # Below is the implementation of Jenkins Configuration as Code. Add a key under ConfigScripts for each configuration area, # where each corresponds to a plugin or section of the UI. Each key (prior to | character) is just a label, and can be any value. # Keys are only used to give the section a meaningful name. The only restriction is they may only contain RFC 1123 \ DNS label - # characters: lowercase letters, numbers, and hyphens. The keys become the name of a configuration yaml file on the master in + # characters: lowercase letters, numbers, and hyphens. The keys become the name of a configuration YAML file on the master in # /var/jenkins_home/casc_configs (by default) and will be processed by the Configuration as Code Plugin. The lines after each | - # become the content of the configuration yaml file. The first line after this is a JCasC root element, eg jenkins, credentials, + # become the content of the configuration YAML file. The first line after this is a JCasC root element, eg jenkins, credentials, # etc. Best reference is https:///configuration-as-code/reference. The example below creates a welcome message: JCasC: enabled: true @@ -82,7 +82,7 @@ Master: # requires additional JavaOpts, ie # JavaOpts: > # -Dcom.sun.management.jmxremote.port=4000 -# -Dcom.sun.management.jmxremote.authenticsate=false +# -Dcom.sun.management.jmxremote.authenticate=false # -Dcom.sun.management.jmxremote.ssl=false # JMXPort: 4000 # List of plugins to be install during Jenkins master start diff --git a/demos/kubernetes-secrets/README.md b/demos/kubernetes-secrets/README.md index dec1fbb2..bee92c82 100644 --- a/demos/kubernetes-secrets/README.md +++ b/demos/kubernetes-secrets/README.md @@ -1,11 +1,13 @@ # Configure Kubernetes secrets for Jenkins configuration-as-code plugin ### Prerequisites + 1. `SECRETS` environment variable should provide a path to mounted secret volume. 2. Kubernetes secrets with all required values. 3. `volumeMounts` and `volumes` directives of Kubernetes manifest should have records for Kubernetes secrets mounts. ### Sample configuration + ``` --- apiVersion: v1 diff --git a/demos/kubernetes/README.md b/demos/kubernetes/README.md index 9b66bfc7..09566d6a 100644 --- a/demos/kubernetes/README.md +++ b/demos/kubernetes/README.md @@ -1,8 +1,8 @@ # Configure Kubernetes plugin Jenkins can be installed in Kubernetes and preconfigured to run jobs (and other -options) in the Kubernetes cluster, using yaml stored in a `ConfigMap`. -See [`config.yml`](config.yml) for the `ConfigMap` definition. +options) in the Kubernetes cluster, using YAML stored in a `ConfigMap`. +See [config.yml](config.yml) for the `ConfigMap` definition. Example installation on Kubernetes: @@ -65,4 +65,4 @@ jenkins: idleMinutes: "1" activeDeadlineSeconds: "120" slaveConnectTimeout: "1000" -``` \ No newline at end of file +``` diff --git a/demos/ldap/README.md b/demos/ldap/README.md index 366c2d83..21f9b69e 100644 --- a/demos/ldap/README.md +++ b/demos/ldap/README.md @@ -20,9 +20,9 @@ jenkins: ## implementation note -`hudson.security.LDAPSecurityRealm` can be configure using it's @DataBoundConstructor parameters without any dedicated +`hudson.security.LDAPSecurityRealm` can be configured using its `@DataBoundConstructor` parameters without any dedicated adapter code. -It is identified as `ldap` as it implements `SecurityRealm` extension point, so we can define a "natural" Symbol name +It is identified as `ldap` as it implements the `SecurityRealm` extension point, so we can define a "natural" symbol name for it. ## sample configuration with search filters diff --git a/demos/mailer/README.md b/demos/mailer/README.md index a3a1d37d..96e698a7 100644 --- a/demos/mailer/README.md +++ b/demos/mailer/README.md @@ -20,10 +20,10 @@ unclassified: ## implementation note -`hudson.task.Mailer.Descriptor` do expose global SMTP configuration parameters. -It is identified as yaml root element `mailer` as this descriptor has a `global.jelly` UI view, so configuration-as-code -assumes it make sense to expose it as a root element extension. +`hudson.task.Mailer.Descriptor` exposes global SMTP configuration parameters. +It is identified as YAML root element `mailer` as this descriptor has a `global.jelly` UI view, so configuration-as-code +assumes it makes sense to expose it as a root element extension. -Descriptor do define setters so we can inject configuration, but for SMTP authentication parameters. +Descriptor defines setters so we can inject configuration, but for SMTP authentication parameters. See https://github.com/jenkinsci/configuration-as-code-plugin/issues/2. See https://github.com/jenkinsci/mailer-plugin/pull/39 diff --git a/demos/plugins/README.md b/demos/plugins/README.md index 8df8602f..f40f0a66 100644 --- a/demos/plugins/README.md +++ b/demos/plugins/README.md @@ -1,11 +1,11 @@ # configure installed plugins Configuration-as-Code can manage plugin installation, assuming Configuration-as-Code-plugin is -intiialy installed (chicken & egg ... ) ! +initially installed (chicken & egg ... )! -yaml configuration can declare a set of required plugins and version. We require -a version as primary goal is to ensure reproducibility, but we also support `latest` -as a version number for your conveninence. +YAML configuration can declare a set of required plugins and version. We require +a version as the primary goal is to ensure reproducibility, but we also support `latest` +as version number for your convenience. For plugins not hosted on an update center, you can set a download URL as version. @@ -19,13 +19,11 @@ plugins: my-custom-plugin: http://download.acme.com/my-custom-plugin-1.0.jpi ``` - - ## implementation note -Plugin installation with specific version require update center to expose -metadata per hosted plugins version, not just latest. +Plugin installation with a specific version requires an update center to expose +metadata per hosted plugin version, not just latest. -Jenkins community update center expose [`plugin-versions.json`](https://updates.jenkins.io/current/plugin-versions.json) +Jenkins community update center exposes [`plugin-versions.json`](https://updates.jenkins.io/current/plugin-versions.json) metadata file. Proprietary update centers might not, in such case you will have -to use download URLs in your yaml file. \ No newline at end of file +to use download URLs in your YAML file. diff --git a/demos/role-strategy-auth/README.md b/demos/role-strategy-auth/README.md index e5365cf1..a2b500da 100644 --- a/demos/role-strategy-auth/README.md +++ b/demos/role-strategy-auth/README.md @@ -1,7 +1,7 @@ # role-strategy-plugin - +Basic configuration of the [Role-based Authorization Strategy plugin](https://plugins.jenkins.io/role-strategy) ## sample -Check out the example yaml file [role-strategy-auth.yaml] which is taken from the plugins' integration test resources \ No newline at end of file +Check out the example YAML file [role-strategy-auth.yaml] which is taken from the plugins' integration test resources diff --git a/demos/sonarqube/README.md b/demos/sonarqube/README.md index c7858d94..e18fa932 100644 --- a/demos/sonarqube/README.md +++ b/demos/sonarqube/README.md @@ -1,18 +1,18 @@ # configure sonar plugin ## sample configuration + Sample configuration for the SonarQube plugin ```yaml jenkins: [...] - unclassified: sonarglobalconfiguration: # mandatory buildWrapperEnabled: true installations: # mandatory - - name: sonarqube # id of the SonarQube configuration - to be used in jobs + - name: sonarqube # id of the SonarQube configuration - to be used in jobs serverUrl: http://sonarqube-service:9000/sq additionalAnalysisProperties: additionalProperties: @@ -20,4 +20,5 @@ unclassified: ``` ## notes + You can add multiple installations. diff --git a/demos/statistics-gatherer/README.md b/demos/statistics-gatherer/README.md index de591154..e099d795 100644 --- a/demos/statistics-gatherer/README.md +++ b/demos/statistics-gatherer/README.md @@ -1,6 +1,7 @@ # configure statistics-gatherer plugin ## sample configuration + Sample configuration for the Statistics Gatherer plugin. ```yaml @@ -16,4 +17,4 @@ unclassified: projectInfo: false buildStepInfo: false scmCheckoutInfo: true -``` \ No newline at end of file +``` diff --git a/demos/tfs/README.md b/demos/tfs/README.md index 69e4d355..dfc07fae 100644 --- a/demos/tfs/README.md +++ b/demos/tfs/README.md @@ -20,4 +20,4 @@ unclassified: ## implementation note -User account name mapping strategy is not yet supported +User account name mapping strategy is not yet supported. diff --git a/demos/warnings/README.md b/demos/warnings/README.md index 6989d9a2..a9cbcfef 100644 --- a/demos/warnings/README.md +++ b/demos/warnings/README.md @@ -23,8 +23,3 @@ unclassified: return builder.buildOptional(); example: "somefile.txt:2:SeriousWarnings:SomethingWentWrong" ``` - - - - - diff --git a/demos/workflow-cps-global-lib/README.md b/demos/workflow-cps-global-lib/README.md index 86066c76..cdd783c8 100644 --- a/demos/workflow-cps-global-lib/README.md +++ b/demos/workflow-cps-global-lib/README.md @@ -10,12 +10,12 @@ jenkins: unclassified: globalLibraries: libraries: - - name: "awesome-lib" + - name: 'awesome-lib' retriever: modernSCM: scm: git: - remote: "https://github.com/jenkins-infra/pipeline-library.git" + remote: 'https://github.com/jenkins-infra/pipeline-library.git' ``` ## Using credentials diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index a2f6154b..6636f462 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -1,6 +1,6 @@ # Developer's documentation -This document describe Configuration-as-Code API and design for plugin developer who are interested in +This document describe Configuration-as-Code API and design for plugin developer who are interested in extending Configuration-as-Code by implementing custom Configurators or re-using the configuration mechanism in another context. @@ -9,35 +9,33 @@ in another context. ## Configurators Third party code to rely on Configuration as Code has to create a key:value hierarchical representation of the target -component and it's sub-components. This representation is defined by the `io.jenkins.plugins.casc.model` package, -and does not force use on any specific file format, despite we focus on `yaml` for Configuration-as-Code. +component and it's sub-components. This representation is defined by the `io.jenkins.plugins.casc.model` package, +and does not force use on any specific file format, despite we focus on `yaml` for Configuration-as-Code. The main API is `Configurator` which encapsulate access to target component data model and how to create/configure it. Such a data-model is exposed to external usage as a set of `Attribute`s via the `Configurator.describe()` method. -Each key in the key:value representation used as configuration input has to match an Attribute. +Each key in the key:value representation used as configuration input has to match an Attribute. ## ConfigurationContext -The configuration process only relies on `ConfigurationContext` to convert key:value representation into a live -component instance. Third party component to use this mechanism can provide a custom context, while +The configuration process only relies on `ConfigurationContext` to convert key:value representation into a live +component instance. Third party component to use this mechanism can provide a custom context, while Configuration-as-Code do rely on registered Jenkins components. - + ConfigurationContext do provide : - tweaks support for deprecated and restricted attributes, as well as unknown input elements - define registry to retrieve Configurator for various component and classes to be configured - offer option to register `Listener`s to get notified about the configuration process and react on errors. -## Yaml support +## YAML support -`io.jenkins.plugins.casc.yaml` package do define the implementation for loading configuration from Yaml sources. -`YamlUtils.loadFrom` encapsulate the yaml parsing and merge process from a set of yaml documents, while `YamlSource` +`io.jenkins.plugins.casc.yaml` package do define the implementation for loading configuration from YAML sources. +`YamlUtils.loadFrom` encapsulate the YAML parsing and merge process from a set of YAML documents, while `YamlSource` abstract the way we load documents from files, url, or any other sources. - - # Extending Configuration-as-Code - + `Configurator` and `Attribute` are the core abstraction of Configuration-as-Code to offer implementation flexibility. Configuration-as-Code do offer implementation based on introspecting Java classes, relying on web UI data-binding mechanisms for DataBound component, and on Java Bean conventions for others (Descriptors, Extensions). @@ -47,8 +45,6 @@ model without relying on introspection, one can extend `BaseConfigurator` and ov control the exposed data model. `Attribute`s exposed by this data model only have to define how to set value on target component and how to retrieve -current value from a live instance (used by `export` feature). Here again Configuration-as-Code do offer as default +current value from a live instance (used by `export` feature). Here again Configuration-as-Code do offer as default implementation a Java Bean compliant implementation, but one could override get and/or set operation with custom code to support alternate mechanisms. - - \ No newline at end of file diff --git a/docs/PLUGINS.md b/docs/PLUGINS.md index 5041311e..b203c251 100644 --- a/docs/PLUGINS.md +++ b/docs/PLUGINS.md @@ -1,16 +1,16 @@ # Guide to plugin developers -Configuration as Code relies on `Describable` and `DataBound` mechanism Jenkins plugin developers are probably already using. -As long as you follow [best practices](https://jenkins.io/doc/developer/plugin-development/pipeline-integration/#constructor-vs-setters) +Configuration as Code relies on `Describable` and `DataBound` mechanism Jenkins plugin developers are probably already using. +As long as you follow [best practices](https://jenkins.io/doc/developer/plugin-development/pipeline-integration/#constructor-vs-setters) using those annotations for data-binding, same attributes will be usable for configuration-as-code. ## Descriptors global configuration -Most of the interesting plugin's configuration you want to expose to end users with configuration-as-code is managed by your plugin's +Most of the interesting plugin's configuration you want to expose to end users with configuration-as-code is managed by your plugin's Descriptor(s) and exposed on web UI with a `global.jelly` view. This is fully supported by configuration-as-code as long as you rely on the exact same `DataBound` mechanism, which isn't a common practice (yet). -In many plugins, `Descriptor#configure()` is implemented by lookup for attributes values from the `JSONObject`. To make your Descriptor +In many plugins, `Descriptor#configure()` is implemented by lookup for attributes values from the `JSONObject`. To make your Descriptor compliant with configuration-as-code, you'll need to expose your configuration attributes as `@DataBoundSetters`. Before you start, make sure the following pre-conditions are met : @@ -34,17 +34,16 @@ Before you start, make sure the following pre-conditions are met : Here's the recommended approach : - Let's consider this Descriptor : -```java +```java public static final class DescriptorImpl extends Descriptor { private String charset; - + /** optional password */ private Secret password; - public boolean configure(StaplerRequest req, JSONObject json) throws FormException { + public boolean configure(StaplerRequest req, JSONObject json) throws FormException { charset = json.getString("charset"); if (json.has("usePassword")) { password = Secret.fromString(nullify(auth.getString("password"))); @@ -57,14 +56,15 @@ public static final class DescriptorImpl extends Descriptor { public String getCharset() { return charset; } } -``` +``` with global.jelly view : + ```xml - + @@ -76,18 +76,19 @@ with global.jelly view : ### Step 1 -Define `@DataBoundSetters` javabean setters for your Descriptor's properties. They should match the getters you already have for +Define `@DataBoundSetters` javabean setters for your Descriptor's properties. They should match the getters you already have for global.jelly data-binding. - -```java + +```java @DataBoundSetter public void setCharset(String charset) { this.charset = charset; - } -``` - -### Step 2 -Create a new Describable object with a `config.jelly` view to own optional attributes. + } +``` + +### Step 2 + +Create a new `Describable` object with a `config.jelly` view to own optional attributes. ```java public class Authentication extends AbstractDescribableImpl { @@ -104,6 +105,7 @@ public class Authentication extends AbstractDescribableImpl { } } ``` + ```xml @@ -113,31 +115,32 @@ public class Authentication extends AbstractDescribableImpl { ``` ### Step 3 -Define a new attribute in your Descriptor to own optional attributes. -For binary compatibility you'll need to maintain the legacy getters as delegates to this new sub-component. -For backward compatibility, use `readResolve` method to create the new nested component from legacy attributes. -```java +Define a new attribute in your Descriptor to own optional attributes. +For binary compatibility you'll need to maintain the legacy getters as delegates to this new sub-component. +For backward compatibility, use `readResolve` method to create the new nested component from legacy attributes. + +```java public static final class DescriptorImpl extends Descriptor { private String charset; - + /** @deprecated use {@link #authentication} */ private transient Secret password; @CheckForNull private Authentication authentication; - + // --- backward compatibility - - /** @deprecated use {@link #getAuthentication()} */ + + /** @deprecated use {@link #getAuthentication()} */ public Secret getPassword() { return authentication != null ? authentication.getPassword() : null; } - + private Object readResolve() { if (this.password != null) { this.authentication = new Authentication(password); } return this; - } + } ``` ### Step 4 @@ -146,22 +149,22 @@ Replace `optionalBlocks` in your jelly view with `optionalProperty` and add the ```xml - + ``` ```java - public Authentication getAuthentication() { return this.authentication; } - + public Authentication getAuthentication() { return this.authentication; } + @DataBoundSetter - public void setAuthentication(Authentication authentication) { this.authentication = authentication; } + public void setAuthentication(Authentication authentication) { this.authentication = authentication; } ``` - + ### Step 5 Rewrite `Descriptor#configure()` implementation to rely on `request.bindJson(this, json)`. You will have to reset attributes to their -default values as a Desciptor is a mutable object, i.e. data-binding won't reset values if they are not present in JSON payload. +default values as a Descriptor is a mutable object, i.e. data-binding won't reset values if they are not present in the JSON payload. ```java public boolean configure(StaplerRequest req, JSONObject json) throws FormException { @@ -174,30 +177,31 @@ default values as a Desciptor is a mutable object, i.e. data-binding won't reset ``` ### Step 6 -If you don't have one already, define a `@Symbol` annotation on your descriptor. This is the name end-user will be able to use to access -your Descriptor for configuration. To avoid collisions with other plugin, prefer using your plugin's artifactId as a symbolic name for your + +If you don't have one already, define a `@Symbol` annotation on your descriptor. This is the name an end user will be able to use to access +your Descriptor for configuration. To avoid collisions with other plugins, prefer using your plugin's artifactId as a symbolic name for your descriptor. -```java +```java @Symbol("foo") public static final class DescriptorImpl extends Descriptor { ``` See [mailer plugin#39](https://github.com/jenkinsci/mailer-plugin/pull/39) for a sample on required changes. +## How to test? -## How to test ? - -Simplest option for you to test JCasC compatibility in your plugin is to introduce a simple test-case. +Simplest option for you to test JCasC compatibility in your plugin is to introduce a simple test case. ### Configuration test + Add configuration-as-code-plugin as a test dependency in your pom.xml: ```xml - io.jenkins - configuration-as-code - 1.5 - test + io.jenkins + configuration-as-code + 1.5 + test ``` @@ -225,28 +229,33 @@ Doing so, you will confirm JCasC is able to introspect your plugin and build the some changes made to your plugin break this configuration model. ### Backward compatibility test -About the later, in case you need to introduce some breaking changes, you can define a backward-compatibility test case + +About the later, in case you need to introduce some breaking changes, you can define a backward compatibility test case ```yaml @Test public void should_be_backward_compatible() throws Exception { ConfigurationAsCode.get().configure(ConfigAsCodeTest.class.getResource("obsolete-configuration-as-code.yml").toString()); assertTrue( /* check plugin has been configured as expected */ ); } ``` + Within this `obsolete-configuration-as-code.yml` configuration file, use the legacy data model in use before the change you introduced, and enable JCasC support for deprecated methods: ```yaml configuration-as-code: deprecated: warn ``` + This will let JCasC consider any `@Deprecated` setter in your component as a valid attribute to be set, enabling backward compatibility, while the canonical JCasC model evolves to match the changes you made. ### Model export test -You also can write a test case to check export from a live instance is well supported : -```java +You also can write a test case to check export from a live instance is well supported: + +```java @Test public void export_configuration() throws Exception { /* Setup jenkins to use plugin */ ConfigurationAsCode.get().export(System.out); } ``` -**TODO** we need to provide some yaml assertion library so that the resulting exported yam stream can be checked for expected content. + +**TODO** we need to provide some yaml assertion library so that the resulting exported yam stream can be checked for expected content. diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index cb245303..3c5de64c 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -5,6 +5,7 @@ 0.1-alpha version of Configuration as Code Plugin has a number of issues that may break your Jenkins instance - details below. DO NOT USE in production environment, only for test. ### Features available: + - Configure (some) plugins via yaml file - Reload configuration from yaml file - manual step, available under `Manage Jenkins`:arrow_right: `Configuration as Code` - Vault support - details in [README](../README.md) @@ -14,12 +15,13 @@ - Matrix (and Matrix Project) Authorization Strategy support ### Limitations: + - Location of yaml file with configuration MUST be provided via environment variable CASC_JENKINS_CONFIG - can be location on disk readable from Jenkins perspective - plugins has to be installed manually (and it must happen before you try to configure them) ### How to use it -1. Add experimental plugins repository ([link](http://updates.jenkins-ci.org/experimental/update-center.json)) +1. Add the experimental plugins repository ([link](http://updates.jenkins.io/experimental/update-center.json)) 2. Prepare jenkins.yaml and store the location in CASC_JENKINS_CONFIG environment variable 3. Install *Configuration as Code* plugin (& all the others plugins you want to use) 4. When installed plugin will look for configuration file and configure Jenkins accordingly diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index 840b39cb..d6550e65 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -1,40 +1,38 @@ # JCasC Requirements - guide for plugin maintainers JCasC is designed so any plugin can be managed without the need to implement any custom -API, but still require plugins to respect some contract, aka "_convention over extension_". +API, but still require plugins to respect some contract, aka "_convention over extension_". This documentation is here to explain plugin maintainers those conventions and provide guidance on expected design. -![CasC is comming](BraceYourselves.jpg) - +![CasC is coming](BraceYourselves.jpg) ## Overview -JCasC relies on ability to introspect jenkins configurable components to build a "data model" -from a live jenkins instance. For this purpose it relies on web UI data-binding conventions. +JCasC relies on the ability to introspect Jenkins configurable components to build a "data model" +from a live Jenkins instance. For this purpose it relies on web UI data-binding conventions. -For legacy reasons, Jenkins do offer multiple ways to support UI data-binding, but the sole +For legacy reasons, Jenkins offers multiple ways to support UI data-binding, but the sole one to be introspection friendly is to offer `@DataBoundSetter` fields or setters in your code. Surprisingly, this is well adopted by most plugins for `Describable` components, but not for `Descriptor`s, despite the exact same mechanism can be used for both. And unfortunately, in -many case the interesting components to offer configuration one want to expose to JCasC +many cases the interesting components to offer configuration one want to expose to JCasC is attached to a Descriptors. - ## Check-list ### Rule 1: don't write code for data-binding Check implementation of `Descriptor#configure(StaplerRequest,JSONObject)` in your descriptors. -This one should **not** use any of the `JSONObject.get*()` methods to set value for an internal -field. Prefer exposing javabean setter methods, and use `req.bindJSON(this,JSONObject)` to rely +This one should **not** use any of the `JSONObject.get*()` methods to set value for an internal +field. Prefer exposing javabean setter methods, and use `req.bindJSON(this,JSONObject)` to rely on introspection-friendly data-binding. -Within a Descriptor such setters don't have to be annotated as `@DataBoundSetter` but we suggest -to do anyway, as it make it clear about the intent for such public methods. +Within a Descriptor such setters don't have to be annotated as `@DataBoundSetter` but we suggest +to do anyway, as it makes their intent more clear. -sample : +sample: ```java public boolean configure(StaplerRequest req, JSONObject json) throws FormException { @@ -46,7 +44,7 @@ public boolean configure(StaplerRequest req, JSONObject json) throws FormExcepti } ``` -to be replaced by : +to be replaced by: ```java public boolean configure(StaplerRequest req, JSONObject json) throws FormException { @@ -70,28 +68,28 @@ public void setReplyToAddress(String address) { } ``` -notes: -- you also need matching getters for jelly view to render current value, but you probably already have them declared. -- use of BulkChange allows to avoid repeated calls to `save()` to actually persist to disk only once fully -configured. -- you might not even need to implement `configure` once [#3669](https://github.com/jenkinsci/jenkins/pull/3669) +Notes: +- You also need matching getters for jelly view to render current value, but you probably already have them declared. +- Use of BulkChange allows to avoid repeated calls to `save()` to actually persist to disk only once fully +configured. +- You might not even need to implement `configure` once [#3669](https://github.com/jenkinsci/jenkins/pull/3669) is merged. ### Rule 2: don't use pseudo-properties for optional You might have a set of fields which only make sense when set altogether, and have jelly view -to use `` based on some boolean pseudo-property to show/hidde the matching section +to use `` based on some boolean pseudo-property to show/hide the matching section in web UI. -Doing so require had-written data-binding code, so based on rule 1 should be prohibited. +Doing so requires had-written data-binding code, so based on rule 1 should be prohibited. Hopefully there's a simple (and arguably better) way to handle this, by just using nested components and group all related fields into an optional sub-element. -sample : +sample: ```xml - @@ -111,8 +109,8 @@ public boolean configure(StaplerRequest req, JSONObject json) throws FormExcepti } } ``` - -to be replaced by : + +to be replaced by: ```xml @@ -125,24 +123,24 @@ private Authentication authentication; With a fresh new `Authentication` Describable class to host `username` and `password`, all the `` body being moved `Authentication/config.jelly` view. - -note: this also require some data migration logic, please read [PLUGINS](PLUGINS.md) for a step +Note: This also requires some data migration logic, please read [PLUGINS](PLUGINS.md) for a step by step migration guide. ### Rule 3: define a test case if you can -Checking support for JCasC is easy as long as your plugin required java 8 / jenkins 2.60+. +Checking support for JCasC is easy as long as your plugin requires Java 8 / Jenkins 2.60+. You just need CasC as a test dependency and a sample yaml file for your component ```xml - io.jenkins - configuration-as-code - 1.0 - test + io.jenkins + configuration-as-code + 1.5 + test ``` + ```java public class ConfigAsCodeTest { @@ -153,15 +151,14 @@ public class ConfigAsCodeTest { assertTrue( /* check plugin has been configured as expected */ ); } ``` - -Benefits for you to write such a testcase : - -- You confirm your plugin is well design regarding JCasC conventions + +Benefits for you to write such a test case: + +- You confirm your plugin is well designed regarding JCasC conventions - You offer users a sample configuration file - You will be able to detect breaking changes that may impact your users ### Rule 4: ping us in case of doubts Really, if you need any assistance getting your plugin to support JCasC, want code review -or anything, ping us on [Gitter](https://gitter.im/jenkinsci/configuration-as-code-plugin). - +or anything, ping us on [Gitter](https://gitter.im/jenkinsci/configuration-as-code-plugin). diff --git a/docs/VAULT.md b/docs/VAULT.md index 06b2b5ae..460087ed 100644 --- a/docs/VAULT.md +++ b/docs/VAULT.md @@ -4,14 +4,14 @@ - An instance of [Hashicorps Vault](https://www.vaultproject.io/) running - Using a docker-based approach is described [here](vault/setup-vault-using-docker.md) -- A Token (or Username and Password) as credentials for accessing Vault +- A token (or username and password) as credentials for accessing Vault ## Put stuff into Vault Creating content in Vault can be done either using curl (not recommended) or using Vault as a client. A simple curl example can be found [here](vault/setup-vault-using-docker.md). Use a native vault application whenever possible. They are available for download [here](https://www.vaultproject.io/downloads.html). Using vault via docker requires you to be inside a running vault docker container. Otherwise the steps are the same. -The below example assume that you have the following environment variables: +The below example assumes that you have the following environment variables: - `$VAULT_TOKEN` containing the token to use as access credentials - `$VAULT_SERVER_URL` containing the URL to your vault server, i.e. `http://vault.domain.local:8200` @@ -29,7 +29,7 @@ SSH_PRIVATE_KEY=@/vault/file/secrets/jenkins_ssh_key Essentially, anything can go into a vault, as long as it's KEY=VALUE formatted. If you work inside a docker container, the above example requires you to have the file `/vault/file/secrets/jenkins_ssh_key` exist inside the docker container. -## Usage +## Usage ```bash $ vault kv get kv/jenkins/master @@ -56,7 +56,5 @@ credentials: ## Current limitations -Due to dependency on BetterCloud's [vault-java-driver](https://github.com/BetterCloud/vault-java-driver), Vault's change default KV backend from v1 to v2 and HTTP endpoints change it's currently unable to use Vault's KV v2 secret store. ([see issue on BetterCloud project](https://github.com/BetterCloud/vault-java-driver/issues/114)) +Due to the dependency on BetterCloud's [vault-java-driver](https://github.com/BetterCloud/vault-java-driver), Vault's change default KV backend from v1 to v2 and HTTP endpoints change it's currently unable to use Vault's KV v2 secret store. ([see issue on BetterCloud project](https://github.com/BetterCloud/vault-java-driver/issues/114)) Be aware which version you use as default dev Vault server, starting from 0.10, it uses KV v2. [See docs](https://www.vaultproject.io/docs/secrets/kv/kv-v2.html) - - diff --git a/docs/migrate.md b/docs/migrate.md index b8da2a28..7ce47221 100644 --- a/docs/migrate.md +++ b/docs/migrate.md @@ -16,4 +16,4 @@ Various samples of plugins' configuration can be found in [demos](../demos) fold ## Export existing configuration -To be able to do that you need to install the plugin manually on your working Jenkins instance and use export function under http://[your_jenkins_url]/configuration-as-code/. Please note that export is not intended to offer a directly usable jenkins.yaml configuration. It can be used for inspiration writting your own, but be aware that export can be partial, or fail for some components. \ No newline at end of file +To be able to do that, you need to install the plugin manually on your working Jenkins instance and use the export function under http://[your_jenkins_url]/configuration-as-code/. Please note that export is not intended to offer a directly usable jenkins.yaml configuration. It can be used for inspiration writing your own, but be aware that export can be partial, or fail for some components. diff --git a/docs/seed-jobs.md b/docs/seed-jobs.md index 8ee025a9..5bee5a63 100644 --- a/docs/seed-jobs.md +++ b/docs/seed-jobs.md @@ -1,6 +1,6 @@ # How to create initial "seed" job -Configuration is not just about setting up jenkins master, it's also about creating an initial set of jobs. +Configuration is not just about setting up Jenkins master, it's also about creating an initial set of jobs. For this purpose, we delegate to the popular [job-dsl-plugin](https://wiki.jenkins.io/display/JENKINS/Job+DSL+Plugin) and run a job-dsl script to create an initial set of jobs. @@ -10,7 +10,7 @@ Typical usage is to rely on a multi-branch, or organization folder job type, so created. So a multi-branch seed job will prepare a master to be fully configured for CI/CD targeting a repository or organization. -Job-DSL plugin uses groovy syntax for it's job configuration DSL, so you'll have to mix yaml and groovy within your +Job-DSL plugin uses groovy syntax for its job configuration DSL, so you'll have to mix YAML and groovy within your configuration-as-code file: ```yaml diff --git a/docs/usageScenarios.md b/docs/usageScenarios.md index dd3dfb00..966d3fe6 100644 --- a/docs/usageScenarios.md +++ b/docs/usageScenarios.md @@ -6,66 +6,60 @@ It's based on some real customer cases and potential users we already have plans In the early stage of the project, we use the scenarios to detect the most important missing features. Later we will use the scenarios as documentation and guidance on how to adapt the configuration as code approach. - ## Manage and version control Jenkins global configuration -A small group of devops engineers are maintaining the company's Jenkins installations. There are a handful of Jenkins master's, an a few hundred slaves. -In general the devops team have full access to all the relevant infrastructure and are responsible for the documentation, compliance, maintenance and continued operation. +A small group of devops engineers are maintaining the company's Jenkins installations. There are a handful of Jenkins masters, and a few hundred agents. +In general the devops team has full access to all the relevant infrastructure and is responsible for the documentation, compliance, maintenance and continued operation. From their IT department they are supplied with base server or client installations for servers and clients, and they apply configuration management practices to maintain the infrastructure. For their Jenkins masters the only configuration not under version control is the Jenkins global configuration. The devops engineers maintain that, together with a set of trusted lead developers. -Their global configuration is most frequently changes related to (in order of most changed first): slave configuration, Jenkins plug-in updates, Jenkins global configuration changes. -All their Jenkins master have common pieces in common (e.g. Artifactory plugin configuration or mail setup) and some instance specific configuration because not all masters are used for the same kind of software development so plug-ins differs. +Their global configuration is most frequently changed related to (in order of most changed first): agent configuration, Jenkins plug-in updates, Jenkins global configuration changes. +All their Jenkins masters have pieces in common (e.g. Artifactory plugin configuration or mail setup) and some instance specific configuration because not all masters are used for the same kind of software development, so plug-ins differ. -In general the devops team agree that the running instances serves as documentation of the configuration themselves - one can always look in the UI. And they can restore from backup. +In general the devops team agrees that the running instances serve as documentation of the configuration themselves - one can always look in the UI. And they can restore from backup. Their problems are always related to changes, where they update a plug-in or change a global configuration that gives unexpected side-effects and is rather hard to debug for the users or devops team members that do not know about it. They try to remember to ping each other on their chat to inform about changes. -The devops team would like more traceability around changes, by completely move to manage the Jenkins global configuration as code and put it under version control. They will like to benefit also from having it as code to be able to spin up test environments and re-use common configuration across instances. +The devops team would like more traceability around changes, by completely moving to manage the Jenkins global configuration as code and put it under version control. They will like to benefit also from having it as code to be able to spin up test environments and re-use common configuration across instances. - -**With Jenkins Configuration as Code plug-in** installed on their master, the devops team will describe all the configuration in the `jenkins.yml` and maintain it through a git repository. When-ever they want to change anything, they will edit the file in git and their production server will refresh the configuration or the list of installed plugi-ns. +**With Jenkins Configuration as Code plug-in** installed on their master, the devops team will describe all the configuration in the `jenkins.yml` and maintain it through a git repository. Whenever they want to change anything, they will edit the file in git and their production server will refresh the configuration or the list of installed plug-ins. From that point on they all have full traceability of all the changes made, and they are able to roll it back easily by reverting commits. They can review the changes simply by looking at the git diffs on commits. -**Getting there and adopting Jenkins Configuration as Code** is also quite easy for the devops team. They will need to install the plug-in, and go the the Configuration as Code menu in Manage Jenkins, and use the export function to create the initial YAML files that represent their current global configuration and plug-in's installed. Export is not intended to offer a directly usable jenkins.yaml configuration. It can be used for inspiration writting your own, but be aware that export can be partial, or fail for some components. - -When the files are in git, they can point to the file in the repository in the Configuration as Code menu, and click reload so Jenkins updates it's configuration accordingly. +**Getting there and adopting Jenkins Configuration as Code** is also quite easy for the devops team. They will need to install the plug-in, and go the the Configuration as Code menu in Manage Jenkins, and use the export function to create the initial YAML files that represent their current global configuration and plug-in's installed. Export is not intended to offer a directly usable jenkins.yaml configuration. It can be used for inspiration writing your own, but be aware that export can be partial, or fail for some components. +When the files are in git, they can point to the file in the repository in the Configuration as Code menu, and click reload so Jenkins updates its configuration accordingly. _Migration step-by-step guide:_ For the exact steps, see this part of the migration guide - TBD when all relevant issues fixed: - * [#6 about reloading configuration when changed](https://github.com/jenkinsci/configuration-as-code-plugin/issues/6) - * [#7 about plug-in installation support](https://github.com/jenkinsci/configuration-as-code-plugin/issues/7) - * [#10 related to hierarchy and re-use of configuration](https://github.com/jenkinsci/configuration-as-code-plugin/issues/10) - * [#32 where to draft a migration to Jenkins Configuration as Code guide](https://github.com/jenkinsci/configuration-as-code-plugin/issues/32) - * [#65 helping users migrate by export existing configuration](https://github.com/jenkinsci/configuration-as-code-plugin/issues/65) - - +* [#6 about reloading configuration when changed](https://github.com/jenkinsci/configuration-as-code-plugin/issues/6) +* [#7 about plug-in installation support](https://github.com/jenkinsci/configuration-as-code-plugin/issues/7) +* [#10 related to hierarchy and re-use of configuration](https://github.com/jenkinsci/configuration-as-code-plugin/issues/10) +* [#32 where to draft a migration to Jenkins Configuration as Code guide](https://github.com/jenkinsci/configuration-as-code-plugin/issues/32) +* [#65 helping users migrate by export existing configuration](https://github.com/jenkinsci/configuration-as-code-plugin/issues/65) ## Jenkins master already in docker -A small full stack developer team have their single Jenkins master running in a container and they orchestrate and deploy it using one of the many container orchestration tools. +A small full stack developer team has their single Jenkins master running in a container and they orchestrate and deploy it using one of the many container orchestration tools. Their Jenkins global configuration is the only piece not under version control, so they depend on the current data in `JENKINS_HOME` to persist the Jenkins global configuration and be able to redeploy the Jenkins master with existing installed plug-ins. They do not care about their historic build data. -**With Jenkins configuration as code plug-in** they can get their missing configuration under version control as well and their installed plug-in combinations and for this little full stack developer team it means they can always just redeploy their Jenkins should something be wrong or failing, and their container orchestration tool will keep their Jenkins master always running for them completely automatically. +**With Jenkins Configuration as Code plug-in** they can get their missing configuration under version control as well and their installed plug-in combinations and for this little full stack developer team it means they can always just redeploy their Jenkins, should something be wrong or failing, and their container orchestration tool will keep their Jenkins master always running for them completely automatically. **Migrating to use the Jenkins Configuration as Code plug-in** is easy if they can live with doing just few manual steps before it is all automated. They need to install the Jenkins Configuration as Code plug-in on their current master, then use the export functionality to create the `plugins.yml` and `jenkins.yml` file for them, which they need to add to a git repository. Then the last thing they need to do is to update their deployment recipe to use the customized Jenkins where the Jenkins Configuration as Code plug-in comes pre-installed, and have the recipe pass the URI to the `jenkins.yml` file when starting Jenkins. After that they only do changes in the configuration file through git, and Jenkins Configuration as Code will refresh global configuration. Should their infrastructure fail their orchestration tool will recreate their master from scratch, but still with the same configuration and without depending on old data sets. -Notice a slight difference between `plugins.yml` as part of the Configuration as Code vs `plugins.txt` known from the Jenkins container setups. The latter one will need to contain all plugins and their dependencies and will usually be auto generated to support starting a Jenkins container with all the plug-ins one like. The `plugin.yml` takes the approach of only mentioning the plugin and their version (or latest) the user really cares about, and Configuration as Code will resolve dependencies for the user. +Notice a slight difference between `plugins.yml` as part of the Configuration as Code vs `plugins.txt` known from the Jenkins container setups. The latter one will need to contain all plugins and their dependencies and will usually be auto generated to support starting a Jenkins container with all the plug-ins one likes. The `plugin.yml` takes the approach of only mentioning the plugin and their version (or latest) the user really cares about, and Configuration as Code will resolve dependencies for the user. This means the team above will only have to maintain the `plugins.yml` entries for plugins they use and then make sure their clean Jenkins they start have the Configuration as Code plug-in installed. That can be done with a customized container image or the one supplied with Configuration as Code plugin. - _Migration step-by-step guide:_ For the exact steps, see this part of the migration guide - TBD when all relevant issues fixed: - * [#6 about reloading configuration when changed](https://github.com/jenkinsci/configuration-as-code-plugin/issues/6) - * [#7 about plug-in installation support](https://github.com/jenkinsci/configuration-as-code-plugin/issues/7) - * [#32 where to draft a migration to Jenkins Configuration as Code guide](https://github.com/jenkinsci/configuration-as-code-plugin/issues/32) - * [#65 helping users migrate by export existing configuration](https://github.com/jenkinsci/configuration-as-code-plugin/issues/65) - * [#70 explaining we need the Docker image the team above need to use](https://github.com/jenkinsci/configuration-as-code-plugin/issues/70) +* [#6 about reloading configuration when changed](https://github.com/jenkinsci/configuration-as-code-plugin/issues/6) +* [#7 about plug-in installation support](https://github.com/jenkinsci/configuration-as-code-plugin/issues/7) +* [#32 where to draft a migration to Jenkins Configuration as Code guide](https://github.com/jenkinsci/configuration-as-code-plugin/issues/32) +* [#65 helping users migrate by export existing configuration](https://github.com/jenkinsci/configuration-as-code-plugin/issues/65) +* [#70 explaining we need the Docker image the team above need to use](https://github.com/jenkinsci/configuration-as-code-plugin/issues/70) diff --git a/docs/vault/setup-vault-using-docker.md b/docs/vault/setup-vault-using-docker.md index dcb53e99..d1f0090c 100644 --- a/docs/vault/setup-vault-using-docker.md +++ b/docs/vault/setup-vault-using-docker.md @@ -82,7 +82,7 @@ 1. Execute: `export VAULT_TOKEN="[VAULT_TOKEN]"` where _Vault Token_ is used 1. Execute: `curl --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{"bar": "Baz"}' http://vault.domain.local:8200/v1/secret/foo` to put test data into the vault. No output is returned if it works -1. Execute: `curl --header "X-Vault-Token: $VAULT_TOKEN" http://vault.domain.local:8200/v1/secret/foo` to get test data from the vault. Expected output is json formatted: +1. Execute: `curl --header "X-Vault-Token: $VAULT_TOKEN" http://vault.domain.local:8200/v1/secret/foo` to get test data from the vault. Expected output is JSON formatted: ```json { @@ -97,4 +97,4 @@ "warnings":null, "auth":null } - ``` \ No newline at end of file + ``` diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/JiraTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/JiraTest.java index 781d2c7f..6267b902 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/JiraTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/JiraTest.java @@ -27,7 +27,7 @@ public class JiraTest { /* JENKINS-52906 * assertEquals(2, descriptor.getSites().length); * assertEquals("http://jira.codehaus.org/", sites[0].getUrl().toString()); - * assertEquals("http://issues.jenkins-ci.org/", sites[1].getUrl().toString()); + * assertEquals("http://issues.jenkins.io/", sites[1].getUrl().toString()); */ assertEquals(1, descriptor.getSites().length); diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/JiraTest.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/JiraTest.yml index 54b081f1..5bfce740 100644 --- a/integrations/src/test/resources/io/jenkins/plugins/casc/JiraTest.yml +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/JiraTest.yml @@ -2,6 +2,6 @@ unclassified: jiraProjectProperty: sites: url: "http://jira.codehaus.org/" -# see https://issues.jenkins-ci.org/browse/JENKINS-52906 +# see https://issues.jenkins.io/browse/JENKINS-52906 # - url: "http://jira.codehaus.org/" -# - url: "http://issues.jenkins-ci.org/" \ No newline at end of file +# - url: "http://issues.jenkins.io/" diff --git a/plugin/pom.xml b/plugin/pom.xml index 4aeffde5..c643f50d 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -15,7 +15,7 @@ Configuration as Code Plugin Manage Jenkins master configuration as code - https://wiki.jenkins-ci.org/display/JENKINS/Configuration+as+Code+Plugin + https://wiki.jenkins.io/display/JENKINS/Configuration+as+Code+Plugin @@ -24,7 +24,7 @@ ${project.version} - + com.bettercloud vault-java-driver @@ -94,7 +94,7 @@ org.jvnet.com4j:com4j - + org.apache.commons:commons-compress diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/core/JNLPLauncherConfigurator.java b/plugin/src/main/java/io/jenkins/plugins/casc/core/JNLPLauncherConfigurator.java index 12b4d118..f679005b 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/core/JNLPLauncherConfigurator.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/core/JNLPLauncherConfigurator.java @@ -26,7 +26,7 @@ public class JNLPLauncherConfigurator extends DataBoundConfigurator i * Update sites doesn't give us metadata about hosted plugins, but "latest" version. So here we workaround this by * searching update site for plugin shortname and we bake specific version's URL based on this URL, assuming this * will be the same but version part in the path. This is a bit fragile but we don't have a better way so far. - * see INFRA-1696 for status + * see INFRA-1696 for status * * @param updateCenter * @param p