Improve documentation/wording further
This commit is contained in:
parent
74c7d1a4e6
commit
b408c01a52
|
|
@ -1,4 +1,4 @@
|
||||||
**Never report security issues in github or in other public channels (Gitter/Twitter/etc.), follow the instruction from [Jenkins Security](https://jenkins.io/security/) to report it on [Jenkins Jira](https://issues.jenkins.io)**
|
**Never report security issues in GitHub or in other public channels (Gitter/Twitter/etc.), follow the instruction from [Jenkins Security](https://jenkins.io/security/) to report it on [Jenkins Jira](https://issues.jenkins.io)**
|
||||||
|
|
||||||
Please make sure to provide following information in your issue description
|
Please make sure to provide following information in your issue description
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
= 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.
|
This document captures the ongoing status of plugins known to be compatible or incompatible.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ Source code contribution is the obvious one but we also need your feedback and i
|
||||||
|
|
||||||
We have our vision for the plugin and we have an experience with maintaining Jenkins instances, but the plugin is not supposed to solve only our problems. Surely we haven't experienced all of them... That's why we want to hear from you.
|
We have our vision for the plugin and we have an experience with maintaining Jenkins instances, but the plugin is not supposed to solve only our problems. Surely we haven't experienced all of them... That's why we want to hear from you.
|
||||||
|
|
||||||
Please use github issues if you need to report a bug or request changes/improvements.
|
Please use GitHub issues if you need to report a bug or request changes/improvements.
|
||||||
Whenever you report a problem please provide information about:
|
Whenever you report a problem please provide information about:
|
||||||
- Plugin version (Configuration as Code plugin as well any other plugin you suspect your problem to be related to)
|
- Plugin version (Configuration as Code plugin as well any other plugin you suspect your problem to be related to)
|
||||||
- Jenkins version
|
- Jenkins version
|
||||||
|
|
@ -16,9 +16,9 @@ Whenever you report a problem please provide information about:
|
||||||
|
|
||||||
### Regarding source code contribution WoW:
|
### Regarding source code contribution WoW:
|
||||||
|
|
||||||
1. Create a github issue for your feature/problem you want to solve
|
1. Create a GitHub issue for your feature/problem you want to solve
|
||||||
2. Implement solution on a branch in your fork
|
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
|
3. Make sure to include issue number in commit message, and make the message speak for itself
|
||||||
4. Once you're done create a 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
|
||||||
|
|
||||||
!! Never push directly to this repository
|
Never push directly to this repository!
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ It has:
|
||||||
* a `describe` method to document the attributes the target component exposes to configuration
|
* a `describe` method to document the attributes the target component exposes to configuration
|
||||||
* a `configure` method to configure the target component
|
* a `configure` method to configure the target component
|
||||||
|
|
||||||
From a YAML node with an associated `Configurator`, configuration-as-code will handle every
|
From a YAML node with an associated `Configurator`, JCasC will handle every
|
||||||
child node in the YAML structure based on the current node's `Attribute`s, as described by the `Configurator`.
|
child node in the YAML structure based on the current node's `Attribute`s, as described by the `Configurator`.
|
||||||
|
|
||||||
## Root Configurator selection
|
## Root Configurator selection
|
||||||
|
|
@ -28,7 +28,7 @@ Root elements are identified by YAML entry name, and a matching
|
||||||
`RootElementConfigurator` is a special interface used to identify a `Configurator` which manages a top level
|
`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 provides a custom `RootElementConfigurator` for the `jenkins` root entry in the YAML document,
|
JCasC 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).
|
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
|
### Attributes
|
||||||
|
|
@ -37,8 +37,8 @@ as well as generic `RootElementConfigurator`s to model [global configuration cat
|
||||||
of [`Attribute`](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/plugin/src/main/java/io/jenkins/plugins/casc/Attribute.java)s
|
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.
|
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 define setter
|
We don't want to expose the whole Jenkins Java API to JCasC. Many components define setter
|
||||||
methods for technical reasons or to support backward compatibility. So configuration-as-code excludes:
|
methods for technical reasons or to support backward compatibility. So JCasC excludes:
|
||||||
|
|
||||||
* setter methods marked as `@Deprecated`
|
* setter methods marked as `@Deprecated`
|
||||||
* setter methods marked as `@Restricted`
|
* setter methods marked as `@Restricted`
|
||||||
|
|
@ -54,18 +54,18 @@ will be detected as attribute named `foo` with target type `Foo` with multiple v
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
Configurator also has to implement the `configure` method to process YAML content and instantiate or configure
|
`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
|
the target component. The actual mechanism depends on the target. JCasC provides generic
|
||||||
mechanisms which cover most Jenkins components.
|
mechanisms which cover most Jenkins components.
|
||||||
|
|
||||||
This generic mechanism assumes Jenkins core components and plugins do follow some conventions, but
|
This generic mechanism assumes Jenkins core components and plugins follow some conventions, but
|
||||||
custom "glue-code" can also be provided as a (temporary) workaround, or to expose a configuration model
|
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 the [credentials](https://plugins.jenkins.io/credentials) 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)
|
[`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
|
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.
|
using the other general purpose JCasC component due to the internal design of this plugin.
|
||||||
|
|
||||||
## General purpose configurators
|
## General purpose configurators
|
||||||
|
|
||||||
|
|
@ -89,9 +89,9 @@ global configuration for Descriptors, to mimic the `global.jelly` UI exposed
|
||||||
to end users on the web UI.
|
to end users on the web UI.
|
||||||
|
|
||||||
Jenkins has hundreds of Descriptors, most of them for internal technical reasons,
|
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.
|
so only the ones that have a `global` view are accessible from JCasC.
|
||||||
|
|
||||||
For Descriptors to work well with configuration-as-code, they need to follow
|
For Descriptors to work well with JCasC, they need to follow
|
||||||
[some design best practices](docs/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.
|
so we expect this will require some evangelism on plugin developers.
|
||||||
|
|
||||||
|
|
|
||||||
26
README.md
26
README.md
|
|
@ -1,4 +1,4 @@
|
||||||
# Jenkins Configuration as Code Plugin
|
# Jenkins Configuration as Code (a.k.a. JCasC) Plugin
|
||||||
|
|
||||||
[](https://ci.jenkins.io/job/Plugins/job/configuration-as-code-plugin/job/master/)
|
[](https://ci.jenkins.io/job/Plugins/job/configuration-as-code-plugin/job/master/)
|
||||||
[](https://plugins.jenkins.io/configuration-as-code)
|
[](https://plugins.jenkins.io/configuration-as-code)
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
View the [wiki](https://wiki.jenkins.io/display/JENKINS/configuration+as+code+plugin) page. See [presentation slides](https://docs.google.com/presentation/d/1VsvDuffinmxOjg0a7irhgJSRWpCzLg_Yskf7Fw7FpBg/edit?usp=sharing) from Jenkins World 2018.
|
View the [wiki](https://wiki.jenkins.io/display/JENKINS/configuration+as+code+plugin) page. See [presentation slides](https://docs.google.com/presentation/d/1VsvDuffinmxOjg0a7irhgJSRWpCzLg_Yskf7Fw7FpBg/edit?usp=sharing) from Jenkins World 2018.
|
||||||
|
|
||||||
Join our Jenkins Configuration as Code (JCasC) office hours meeting scheduled for every second Wednesday. Use the Hangout on Air link from our [Gitter](https://gitter.im/jenkinsci/configuration-as-code-plugin) chat channel. As an alternative, use the link from the [invitation](https://calendar.google.com/event?action=TEMPLATE&tmeid=MmdwdTE1cTFvaGw1NGUycGxqdWUwcXExaWFfMjAxODA3MjVUMDcwMDAwWiBld2VAcHJhcW1hLm5ldA&tmsrc=ewe%40praqma.net&scp=ALL). See previous [meeting minutes](https://docs.google.com/document/d/1Hm07Q1egWL6VVAqNgu27bcMnqNZhYJmXKRvknVw4Y84/edit?usp=sharing).
|
Join our Jenkins Configuration as Code office hours meeting scheduled for every second Wednesday. Use the Hangout on Air link from our [Gitter](https://gitter.im/jenkinsci/configuration-as-code-plugin) chat channel. As an alternative, use the link from the [invitation](https://calendar.google.com/event?action=TEMPLATE&tmeid=MmdwdTE1cTFvaGw1NGUycGxqdWUwcXExaWFfMjAxODA3MjVUMDcwMDAwWiBld2VAcHJhcW1hLm5ldA&tmsrc=ewe%40praqma.net&scp=ALL). See previous [meeting minutes](https://docs.google.com/document/d/1Hm07Q1egWL6VVAqNgu27bcMnqNZhYJmXKRvknVw4Y84/edit?usp=sharing).
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
|
|
@ -19,13 +19,13 @@ 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
|
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.
|
you know Jenkins internals, and are confident in writing groovy scripts on top of Jenkins API.
|
||||||
|
|
||||||
The 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
|
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.
|
expert, just translating into _code_ a configuration process one is used to executing in the web UI.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
This plugin aims to replace above user-interface based configuration with the below text based configuration.
|
This plugin aims to replace above user interface based configuration with the below text based configuration.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jenkins:
|
jenkins:
|
||||||
|
|
@ -41,13 +41,13 @@ jenkins:
|
||||||
```
|
```
|
||||||
|
|
||||||
In addition, we want to have a well documented syntax file, and tooling to assist in writing and testing,
|
In addition, we want to have a well documented syntax file, and tooling to assist in writing and testing,
|
||||||
so end-users have full guidance in using this tool-set and do not have to search for examples on the Internet.
|
so end users have full guidance in using this tool set and do not have to search for examples on the Internet.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
First, start a Jenkins instance with the [configuration-as-code](https://plugins.jenkins.io/configuration-as-code) plugin 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).
|
- Those running Jenkins as a [Docker](https://github.com/jenkinsci/docker) container (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).
|
||||||
|
|
||||||
Second, the plugin looks for the `CASC_JENKINS_CONFIG` environment variable. The variable can point to any of the following:
|
Second, the plugin looks for the `CASC_JENKINS_CONFIG` environment variable. The variable can point to any of the following:
|
||||||
|
|
||||||
|
|
@ -200,12 +200,12 @@ Also see [demos](demos) folder with various samples.
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The configuration file format depends on the version of jenkins-core and installed plugins.
|
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
|
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
|
## Handling Secrets
|
||||||
|
|
||||||
Currently, you can provide initial secrets to Configuration-as-Code that all rely on <key,value>
|
Currently, you can provide initial secrets to JCasC that all rely on <key,value>
|
||||||
substitution of strings in the 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}`.
|
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}`.
|
||||||
|
|
||||||
|
|
@ -251,7 +251,7 @@ Prerequisites:
|
||||||
- The environment variable `CASC_VAULT_FILE` is optional, provides a way for the other variables to be read from a file instead of environment variables.
|
- The environment variable `CASC_VAULT_FILE` is optional, provides a way for the other variables to be read from a file instead of environment variables.
|
||||||
- The environment variable `CASC_VAULT_ENGINE_VERSION` is optional. If unset, your vault path is assumed to be using kv version 2. If your vault path uses engine version 1, set this variable to `1`.
|
- The environment variable `CASC_VAULT_ENGINE_VERSION` is optional. If unset, your vault path is assumed to be using kv version 2. If your vault path uses engine version 1, set this variable to `1`.
|
||||||
|
|
||||||
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.
|
If the environment variables `CASC_VAULT_URL` and `CASC_VAULT_PATHS` are present, JCasC 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 a file.
|
You can also provide a `CASC_VAULT_FILE` environment variable where you load the secrets from a file.
|
||||||
|
|
||||||
|
|
@ -300,13 +300,13 @@ secrets:
|
||||||
|
|
||||||
Status: `BETA`
|
Status: `BETA`
|
||||||
|
|
||||||
We currently do support plugin installation but it will remain in `beta` for the foreseeable future. Generally
|
We currently support plugin installation but it will remain in `beta` for the foreseeable future. Generally
|
||||||
we recommend that you package your plugins with your Jenkins distribution as plugin installation often requires a
|
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.
|
restart and can cause problems with plugin dependencies. So if you want to try it, you can.
|
||||||
|
|
||||||
The current implementation does 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)
|
Example: (Requires Configuration as Code plugin version > 0.7-alpha)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
plugins:
|
plugins:
|
||||||
|
|
@ -321,6 +321,6 @@ Most plugins should be supported out-of-the-box, or maybe require some minimal c
|
||||||
|
|
||||||
## Jenkins Enhancement Proposal
|
## 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
|
[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:
|
of the Jenkins project. The proposal was accepted. :tada:
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ Basic configuration of the [Active Directory plugin](https://wiki.jenkins.io/dis
|
||||||
|
|
||||||
## sample configuration
|
## sample configuration
|
||||||
|
|
||||||
For Active Directory Plugin version 2.12 and up:
|
For plugin version 2.12 and up:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jenkins:
|
jenkins:
|
||||||
SecurityRealm:
|
SecurityRealm:
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ Alauda DevOps Sync plugin configuration belongs under `unclassified` root elemen
|
||||||
## sample configuration
|
## sample configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jenkins:
|
jenkins:
|
||||||
[...]
|
[...]
|
||||||
unclassified:
|
unclassified:
|
||||||
[...]
|
[...]
|
||||||
alaudaSync:
|
alaudaSync:
|
||||||
enabled: true
|
enabled: true
|
||||||
jenkinsService: jenkins
|
jenkinsService: jenkins
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
# configure config-file-provider files
|
# configure config-file-provider files
|
||||||
|
|
||||||
## sample configuration
|
## sample configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jenkins:
|
jenkins:
|
||||||
[...]
|
[...]
|
||||||
unclassified:
|
unclassified:
|
||||||
globalConfigFiles:
|
globalConfigFiles:
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
credentials:
|
credentials:
|
||||||
system:
|
system:
|
||||||
domainCredentials:
|
domainCredentials:
|
||||||
- domain :
|
- domain:
|
||||||
name: "test.com"
|
name: "test.com"
|
||||||
description: "test.com domain"
|
description: "test.com domain"
|
||||||
specifications:
|
specifications:
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,6 @@ jenkins:
|
||||||
|
|
||||||
## implementation note
|
## implementation note
|
||||||
|
|
||||||
Jenkins singleton doesn't offer any `setClouds` method. So here we rely on a pseudo-property implemented by a dedicated
|
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
|
`Attribute` to add the configured clouds to `Jenkins.clouds`. The current implementation only adds the configured cloud
|
||||||
if it doesn't exists yet.
|
if it doesn't exists yet.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# configure Amazon EC2 Plugin
|
# configure Amazon EC2 plugin
|
||||||
|
|
||||||
https://wiki.jenkins.io/display/JENKINS/Amazon+EC2+Plugin
|
https://wiki.jenkins.io/display/JENKINS/Amazon+EC2+Plugin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ tool:
|
||||||
## implementation note
|
## implementation note
|
||||||
|
|
||||||
Here we rely on `hudson.tools.ToolDescriptor.setInstallations`, so same applies to all ToolInstallations.
|
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
|
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.
|
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
|
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
|
an array - blame Java to not just accept any `Iterable` - so we need to detect this scenario and do the type
|
||||||
conversion.
|
conversion.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ Basic configuration of the [GitHub plugin](https://wiki.jenkins.io/display/JENKI
|
||||||
## sample configuration
|
## sample configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jenkins:
|
jenkins:
|
||||||
[...]
|
[...]
|
||||||
unclassified:
|
unclassified:
|
||||||
githubpluginconfig:
|
githubpluginconfig:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
## sample configuration
|
## sample configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jenkins:
|
jenkins:
|
||||||
[...]
|
[...]
|
||||||
credentials:
|
credentials:
|
||||||
system:
|
system:
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ Basic configuration of gitscm
|
||||||
## sample configuration
|
## sample configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jenkins:
|
jenkins:
|
||||||
[...]
|
[...]
|
||||||
unclassified:
|
unclassified:
|
||||||
gitscm:
|
gitscm:
|
||||||
globalConfigName: jenkins
|
globalConfigName: jenkins
|
||||||
globalConfigEmail: jenkins@domain.local
|
globalConfigEmail: jenkins@domain.local
|
||||||
createAccountBasedOnEmail: true
|
createAccountBasedOnEmail: true
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# configure Metrics Graphite Plugin
|
# configure Metrics Graphite plugin
|
||||||
|
|
||||||
## sample configuration
|
## sample configuration
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ Many of the plugins are actually configured in the same section, but to configur
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jenkins:
|
jenkins:
|
||||||
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code Plugin\n\n"
|
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code plugin\n\n"
|
||||||
numExecutors: 5
|
numExecutors: 5
|
||||||
scmCheckoutRetryCount: 2
|
scmCheckoutRetryCount: 2
|
||||||
mode: NORMAL
|
mode: NORMAL
|
||||||
|
|
@ -27,7 +27,7 @@ jenkins:
|
||||||
Welcome to our build server.
|
Welcome to our build server.
|
||||||
|
|
||||||
This Jenkins is 100% configured and managed 'as code'.
|
This Jenkins is 100% configured and managed 'as code'.
|
||||||
Config is now mostly handled by 'Jenkins Configuration as Code Plugin' (JCasC).
|
Config is now mostly handled by the 'Jenkins Configuration as Code' (JCasC) plugin.
|
||||||
JCasC config can be found in the jenkins.yaml file in the $JENKINS_HOME/casc/ folder.
|
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,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
jenkins:
|
jenkins:
|
||||||
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code Plugin\n\n"
|
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code plugin\n\n"
|
||||||
numExecutors: 5
|
numExecutors: 5
|
||||||
scmCheckoutRetryCount: 2
|
scmCheckoutRetryCount: 2
|
||||||
mode: NORMAL
|
mode: NORMAL
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
# Configure seed jobs
|
# Configure seed jobs
|
||||||
|
|
||||||
As explained in [seed-jobs.md](../../docs/seed-jobs.md), `jobs` declaration is useful to create an initial set of jobs.
|
As explained in [seed-jobs.md](../../docs/seed-jobs.md), `jobs` declaration is useful to create an initial set of jobs.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
The Job DSL plugin uses groovy syntax for its 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.
|
||||||
configuration-as-code file.
|
|
||||||
|
|
||||||
## sample configurations
|
## sample configurations
|
||||||
|
|
||||||
|
|
@ -17,7 +16,6 @@ configuration-as-code file.
|
||||||
|
|
||||||
## implementation note
|
## implementation note
|
||||||
|
|
||||||
- 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.
|
The main issue with the `jobs` declaration for now is the difference in the `Traits` declaration due to [JENKINS-45504](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.
|
||||||
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.
|
|
||||||
|
|
||||||
- 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)
|
Job DSL only allows `periodic(int min)` for configuring a 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)
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
jenkins:
|
jenkins:
|
||||||
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code Plugin\n\n"
|
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code plugin\n\n"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- script: >
|
- script: >
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Install the Plugin using the Official Jenkins helm chart
|
# Install the plugin using the official Jenkins helm chart
|
||||||
|
|
||||||
## Preparation
|
## Preparation
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ Master:
|
||||||
# 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.
|
# 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
|
# 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 |
|
# /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://<jenkins_url>/configuration-as-code/reference. The example below creates a welcome message:
|
# etc. Best reference is https://<jenkins_url>/configuration-as-code/reference. The example below creates a welcome message:
|
||||||
JCasC:
|
JCasC:
|
||||||
|
|
@ -39,7 +39,7 @@ Master:
|
||||||
Sidecars:
|
Sidecars:
|
||||||
configAutoReload:
|
configAutoReload:
|
||||||
# If enabled: true, Jenkins Configuration as Code will be reloaded on-the-fly without a reboot. If false or not-specified,
|
# If enabled: true, Jenkins Configuration as Code will be reloaded on-the-fly without a reboot. If false or not-specified,
|
||||||
# jcasc changes will cause a reboot and will only be applied at the subsequent start-up. Auto-reload uses the Jenkins CLI
|
# JCasC changes will cause a reboot and will only be applied at the subsequent start-up. Auto-reload uses the Jenkins CLI
|
||||||
# over SSH to reapply config when changes to the ConfigScripts are detected. The admin user (or account you specify in
|
# over SSH to reapply config when changes to the ConfigScripts are detected. The admin user (or account you specify in
|
||||||
# Master.AdminUser) will have a random SSH private key (RSA 4096) assigned unless you specify OwnSshKey: true. This will be saved to a k8s secret.
|
# Master.AdminUser) will have a random SSH private key (RSA 4096) assigned unless you specify OwnSshKey: true. This will be saved to a k8s secret.
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
@ -53,7 +53,7 @@ Master:
|
||||||
# cpu: 50m
|
# cpu: 50m
|
||||||
# memory: 50Mi
|
# memory: 50Mi
|
||||||
# SSH port value can be set to any unused TCP port. The default, 1044, is a non-standard SSH port that has been chosen at random.
|
# SSH port value can be set to any unused TCP port. The default, 1044, is a non-standard SSH port that has been chosen at random.
|
||||||
# Is only used to reload jcasc config from the sidecar container running in the Jenkins master pod.
|
# Is only used to reload JCasC config from the sidecar container running in the Jenkins master pod.
|
||||||
# This TCP port will not be open in the pod (unless you specifically configure this), so Jenkins will not be
|
# This TCP port will not be open in the pod (unless you specifically configure this), so Jenkins will not be
|
||||||
# accessible via SSH from outside of the pod. Note if you use non-root pod privileges (RunAsUser & FsGroup),
|
# accessible via SSH from outside of the pod. Note if you use non-root pod privileges (RunAsUser & FsGroup),
|
||||||
# this must be > 1024:
|
# this must be > 1024:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Default values for jenkins.
|
# Default values for Jenkins.
|
||||||
# This is a YAML-formatted file.
|
# This is a YAML-formatted file.
|
||||||
# Declare name/value pairs to be passed into your templates.
|
# Declare name/value pairs to be passed into your templates.
|
||||||
# name: value
|
# name: value
|
||||||
|
|
@ -30,7 +30,7 @@ Master:
|
||||||
# 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.
|
# 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
|
# 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 |
|
# /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://<jenkins_url>/configuration-as-code/reference. The example below creates a welcome message:
|
# etc. Best reference is https://<jenkins_url>/configuration-as-code/reference. The example below creates a welcome message:
|
||||||
JCasC:
|
JCasC:
|
||||||
|
|
@ -45,7 +45,7 @@ Master:
|
||||||
Sidecars:
|
Sidecars:
|
||||||
configAutoReload:
|
configAutoReload:
|
||||||
# If enabled: true, Jenkins Configuration as Code will be reloaded on-the-fly without a reboot. If false or not-specified,
|
# If enabled: true, Jenkins Configuration as Code will be reloaded on-the-fly without a reboot. If false or not-specified,
|
||||||
# jcasc changes will cause a reboot and will only be applied at the subsequent start-up. Auto-reload uses the Jenkins CLI
|
# JCasC changes will cause a reboot and will only be applied at the subsequent start-up. Auto-reload uses the Jenkins CLI
|
||||||
# over SSH to reapply config when changes to the ConfigScripts are detected. The admin user (or account you specify in
|
# over SSH to reapply config when changes to the ConfigScripts are detected. The admin user (or account you specify in
|
||||||
# Master.AdminUser) will have a random SSH private key (RSA 4096) assigned unless you specify OwnSshKey: true. This will be saved to a k8s secret.
|
# Master.AdminUser) will have a random SSH private key (RSA 4096) assigned unless you specify OwnSshKey: true. This will be saved to a k8s secret.
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
@ -59,7 +59,7 @@ Master:
|
||||||
# cpu: 50m
|
# cpu: 50m
|
||||||
# memory: 50Mi
|
# memory: 50Mi
|
||||||
# SSH port value can be set to any unused TCP port. The default, 1044, is a non-standard SSH port that has been chosen at random.
|
# SSH port value can be set to any unused TCP port. The default, 1044, is a non-standard SSH port that has been chosen at random.
|
||||||
# Is only used to reload jcasc config from the sidecar container running in the Jenkins master pod.
|
# Is only used to reload JCasC config from the sidecar container running in the Jenkins master pod.
|
||||||
# This TCP port will not be open in the pod (unless you specifically configure this), so Jenkins will not be
|
# This TCP port will not be open in the pod (unless you specifically configure this), so Jenkins will not be
|
||||||
# accessible via SSH from outside of the pod. Note if you use non-root pod privileges (RunAsUser & FsGroup),
|
# accessible via SSH from outside of the pod. Note if you use non-root pod privileges (RunAsUser & FsGroup),
|
||||||
# this must be > 1024:
|
# this must be > 1024:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Configure Kubernetes secrets for Jenkins configuration-as-code plugin
|
# Configure Kubernetes secrets for Jenkins Configuration as Code plugin
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ kind: StatefulSet
|
||||||
- name: CASC_JENKINS_CONFIG
|
- name: CASC_JENKINS_CONFIG
|
||||||
value: /var/jenkins_config/jenkins.yaml
|
value: /var/jenkins_config/jenkins.yaml
|
||||||
# With the help of SECRETS environment variable
|
# With the help of SECRETS environment variable
|
||||||
# we points Jenkins configuration-as-code plugin the location of the secrets
|
# we point Jenkins Configuration as Code plugin the location of the secrets
|
||||||
- name: SECRETS
|
- name: SECRETS
|
||||||
value: /secrets/jenkins
|
value: /secrets/jenkins
|
||||||
...
|
...
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ jenkins:
|
||||||
|
|
||||||
`hudson.security.LDAPSecurityRealm` can be configured using its `@DataBoundConstructor` parameters without any dedicated
|
`hudson.security.LDAPSecurityRealm` can be configured using its `@DataBoundConstructor` parameters without any dedicated
|
||||||
adapter code.
|
adapter code.
|
||||||
It is identified as `ldap` as it implements the `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.
|
for it.
|
||||||
|
|
||||||
## sample configuration with search filters
|
## sample configuration with search filters
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ unclassified:
|
||||||
## implementation note
|
## implementation note
|
||||||
|
|
||||||
`hudson.task.Mailer.Descriptor` exposes global SMTP configuration parameters.
|
`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
|
It is identified as YAML root element `mailer` as this descriptor has a `global.jelly` UI view, so JCasC
|
||||||
assumes it makes sense to expose it as a root element extension.
|
assumes it makes sense to expose it as a root element extension.
|
||||||
|
|
||||||
Descriptor defines setters so we can inject configuration, but for SMTP authentication parameters.
|
Descriptor defines setters so we can inject configuration, but for SMTP authentication parameters.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ jenkins:
|
||||||
declineOfferDuration: 600
|
declineOfferDuration: 600
|
||||||
description: "My Mesos Cloud"
|
description: "My Mesos Cloud"
|
||||||
frameworkName: "Jenkins Framework"
|
frameworkName: "Jenkins Framework"
|
||||||
# jenkinsURL is a mandatory field, jenkins fails without it. See https://github.com/jenkinsci/configuration-as-code-plugin/issues/578
|
# jenkinsURL is a mandatory field, Jenkins fails without it. See https://github.com/jenkinsci/configuration-as-code-plugin/issues/578
|
||||||
jenkinsURL: "https://jenkins.mesos.cloud"
|
jenkinsURL: "https://jenkins.mesos.cloud"
|
||||||
master: 1.2.3.4:8000
|
master: 1.2.3.4:8000
|
||||||
nativeLibraryPath: "/usr/lib/libmesos.so"
|
nativeLibraryPath: "/usr/lib/libmesos.so"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# configure installed plugins
|
# configure installed plugins
|
||||||
|
|
||||||
Configuration-as-Code can manage plugin installation, assuming Configuration-as-Code-plugin is
|
JCasC can manage plugin installation, assuming the Configuration as Code plugin is
|
||||||
initially installed (chicken & egg ... )!
|
initially installed (chicken & egg ... )!
|
||||||
|
|
||||||
YAML configuration can declare a set of required plugins and version. We require
|
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`
|
a version as the primary goal is to ensure reproducibility, but we also support `latest`
|
||||||
|
|
@ -22,7 +22,7 @@ plugins:
|
||||||
## implementation note
|
## implementation note
|
||||||
|
|
||||||
Plugin installation with a specific version requires an update center to expose
|
Plugin installation with a specific version requires an update center to expose
|
||||||
metadata per hosted plugin version, not just latest.
|
metadata per hosted plugin version, not just latest.
|
||||||
|
|
||||||
Jenkins community update center exposes [`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
|
metadata file. Proprietary update centers might not, in such case you will have
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# configure simple-theme-plugin Plugin
|
# configure simple-theme-plugin plugin
|
||||||
|
|
||||||
## sample configuration
|
## sample configuration
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Configure Slack
|
# Configure Slack
|
||||||
|
|
||||||
Basic configuration of the [Slack Plugin](https://wiki.jenkins.io/display/JENKINS/Slack+Plugin)
|
Basic configuration of the [Slack plugin](https://wiki.jenkins.io/display/JENKINS/Slack+Plugin)
|
||||||
|
|
||||||
## Sample configuration
|
## Sample configuration
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# example of how to configure terraform plugin
|
# example of how to configure terraform plugin
|
||||||
|
|
||||||
to test it from root of the current repository:
|
to test it from root of the current repository:
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
jenkins:
|
jenkins:
|
||||||
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code Plugin\n\n"
|
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code plugin\n\n"
|
||||||
|
|
||||||
tool:
|
tool:
|
||||||
terraforminstallation:
|
terraforminstallation:
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ TFS plugin configuration belongs under `unclassified` root element
|
||||||
## sample configuration
|
## sample configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jenkins:
|
jenkins:
|
||||||
[...]
|
[...]
|
||||||
unclassified:
|
unclassified:
|
||||||
teampluginglobalconfig:
|
teampluginglobalconfig:
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,50 @@
|
||||||
# Developer's documentation
|
# Developer documentation
|
||||||
|
|
||||||
This document describe Configuration-as-Code API and design for plugin developer who are interested in
|
This document describes the JCasC API and design for plugin developers who are interested in
|
||||||
extending Configuration-as-Code by implementing custom Configurators or re-using the configuration mechanism
|
extending JCasC by implementing custom Configurators or re-using the configuration mechanism
|
||||||
in another context.
|
in another context.
|
||||||
|
|
||||||
# Using Configuration-as-Code
|
# Using Configuration as Code
|
||||||
|
|
||||||
## Configurators
|
## Configurators
|
||||||
|
|
||||||
Third party code to rely on Configuration as Code has to create a key:value hierarchical representation of the target
|
Third party code to rely on JCasC 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,
|
component and its 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.
|
and does not force use on any specific file format, despite we focus on YAML.
|
||||||
|
|
||||||
The main API is `Configurator` which encapsulate access to target component data model and how to create/configure it.
|
The main API is the `Configurator` which encapsulates access to the 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.
|
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
|
## ConfigurationContext
|
||||||
|
|
||||||
The configuration process only relies on `ConfigurationContext` to convert key:value representation into a live
|
The configuration process only relies on `ConfigurationContext` to convert the key:value representation into a live
|
||||||
component instance. Third party component to use this mechanism can provide a custom context, while
|
component instance. Third party components to use this mechanism can provide a custom context, while
|
||||||
Configuration-as-Code do rely on registered Jenkins components.
|
JCasC relies on registered Jenkins components.
|
||||||
|
|
||||||
ConfigurationContext do provide :
|
`ConfigurationContext` provides:
|
||||||
|
|
||||||
- tweaks support for deprecated and restricted attributes, as well as unknown input elements
|
- 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
|
- defines 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.
|
- offers 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.
|
`io.jenkins.plugins.casc.yaml` package defines the implementation for loading the configuration from YAML sources.
|
||||||
`YamlUtils.loadFrom` encapsulate the YAML parsing and merge process from a set of YAML documents, while `YamlSource`
|
`YamlUtils.loadFrom` encapsulates 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.
|
abstracts the way we load documents from files, URLs, or any other sources.
|
||||||
|
|
||||||
# Extending Configuration-as-Code
|
# Extending Configuration as Code
|
||||||
|
|
||||||
`Configurator` and `Attribute` are the core abstraction of Configuration-as-Code to offer implementation flexibility.
|
`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
|
JCasC offers an 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).
|
mechanisms for `DataBound` component, and on JavaBean conventions for others (Descriptors, Extensions).
|
||||||
|
|
||||||
To implement Configurator for some component which doesn't fit into this model, or to control the exposed data
|
To implement Configurator for some component which doesn't fit into this model, or to control the exposed data
|
||||||
model without relying on introspection, one can extend `BaseConfigurator` and override the `describe()` method to
|
model without relying on introspection, one can extend `BaseConfigurator` and override the `describe()` method to
|
||||||
control the exposed data model.
|
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
|
`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 JCasC offers as default
|
||||||
implementation a Java Bean compliant implementation, but one could override get and/or set operation with custom
|
implementation a JavaBean compliant implementation, but one could override get and/or set operation with custom
|
||||||
code to support alternate mechanisms.
|
code to support alternate mechanisms.
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,16 @@ using those annotations for data-binding, same attributes will be usable for con
|
||||||
|
|
||||||
## Descriptors global configuration
|
## 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 JCasC 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
|
`Descriptor`(s) and exposed on web UI with a `global.jelly` view. This is fully supported by JCasC as long as you rely on
|
||||||
the exact same `DataBound` mechanism, which isn't a common practice (yet).
|
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`.
|
compliant with JCasC, you'll need to expose your configuration attributes as `@DataBoundSetters`.
|
||||||
|
|
||||||
Before you start, make sure the following pre-conditions are met :
|
Before you start, make sure the following pre-conditions are met:
|
||||||
|
|
||||||
- the parent pom version of your plugin is aligned with the Configuration as Code [parent pom version](/pom.xml).
|
- The parent pom version of your plugin is aligned with the Configuration as Code [parent pom version](/pom.xml).
|
||||||
```xml
|
```xml
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.jenkins-ci.plugins</groupId>
|
<groupId>org.jenkins-ci.plugins</groupId>
|
||||||
|
|
@ -24,7 +24,7 @@ Before you start, make sure the following pre-conditions are met :
|
||||||
<relativePath />
|
<relativePath />
|
||||||
</parent>
|
</parent>
|
||||||
```
|
```
|
||||||
- the jenkins core version and the java level of your plugin are aligned with the Configuration as Code plugin versions (also in the [pom.xml](/pom.xml)).
|
- The Jenkins core version and the Java level of your plugin are aligned with the Configuration as Code plugin versions (also in the [pom.xml](/pom.xml)).
|
||||||
```xml
|
```xml
|
||||||
<properties>
|
<properties>
|
||||||
<jenkins.version>THE_JENKINS_CORE_VERSION_HERE</jenkins.version>
|
<jenkins.version>THE_JENKINS_CORE_VERSION_HERE</jenkins.version>
|
||||||
|
|
@ -32,9 +32,9 @@ Before you start, make sure the following pre-conditions are met :
|
||||||
</properties>
|
</properties>
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's the recommended approach :
|
Here's the recommended approach:
|
||||||
|
|
||||||
Let's consider this Descriptor :
|
Lets consider this `Descriptor`:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public static final class DescriptorImpl extends Descriptor<Foo> {
|
public static final class DescriptorImpl extends Descriptor<Foo> {
|
||||||
|
|
@ -58,7 +58,7 @@ public static final class DescriptorImpl extends Descriptor<Foo> {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
with global.jelly view :
|
with global.jelly view:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
|
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
|
||||||
|
|
@ -76,8 +76,8 @@ with global.jelly view :
|
||||||
|
|
||||||
### Step 1
|
### 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.
|
`global.jelly` data-binding.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@DataBoundSetter
|
@DataBoundSetter
|
||||||
|
|
@ -116,7 +116,7 @@ public class Authentication extends AbstractDescribableImpl<PAuthentication> {
|
||||||
|
|
||||||
### Step 3
|
### Step 3
|
||||||
|
|
||||||
Define a new attribute in your Descriptor to own optional attributes.
|
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 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.
|
For backward compatibility, use `readResolve` method to create the new nested component from legacy attributes.
|
||||||
|
|
||||||
|
|
@ -145,7 +145,8 @@ public static final class DescriptorImpl extends Descriptor<FooBar> {
|
||||||
|
|
||||||
### Step 4
|
### Step 4
|
||||||
|
|
||||||
Replace `optionalBlocks` in your jelly view with `optionalProperty` and add the required DataBound accessors
|
Replace `optionalBlocks` in your jelly view with `optionalProperty` and add the required `DataBound` accessors
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<f:entry title="${%Charset}" field="charset">
|
<f:entry title="${%Charset}" field="charset">
|
||||||
<f:textbox />
|
<f:textbox />
|
||||||
|
|
@ -164,7 +165,7 @@ Replace `optionalBlocks` in your jelly view with `optionalProperty` and add the
|
||||||
### Step 5
|
### Step 5
|
||||||
|
|
||||||
Rewrite `Descriptor#configure()` implementation to rely on `request.bindJson(this, json)`. You will have to reset attributes to their
|
Rewrite `Descriptor#configure()` implementation to rely on `request.bindJson(this, json)`. You will have to reset attributes to their
|
||||||
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.
|
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
|
```java
|
||||||
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
|
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
|
||||||
|
|
@ -179,7 +180,7 @@ default values as a Descriptor is a mutable object, i.e. data-binding won't rese
|
||||||
### Step 6
|
### Step 6
|
||||||
|
|
||||||
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
|
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
|
your `Descriptor` for configuration. To avoid collisions with other plugins, prefer using your plugin's artifactId as a symbolic name for your
|
||||||
descriptor.
|
descriptor.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
|
@ -195,7 +196,8 @@ Simplest option for you to test JCasC compatibility in your plugin is to introdu
|
||||||
|
|
||||||
### Configuration test
|
### Configuration test
|
||||||
|
|
||||||
Add configuration-as-code-plugin as a test dependency in your pom.xml:
|
Add the Configuration as Code plugin as a test dependency in your pom.xml:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.jenkins</groupId>
|
<groupId>io.jenkins</groupId>
|
||||||
|
|
@ -205,7 +207,8 @@ Add configuration-as-code-plugin as a test dependency in your pom.xml:
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
Add a new test case to load a reference configuration yaml file designed to set configurable properties of your plugin
|
Add a new test case to load a reference configuration YAML file designed to set configurable properties of your plugin
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import io.jenkins.plugins.casc.ConfigurationAsCode;
|
import io.jenkins.plugins.casc.ConfigurationAsCode;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
|
@ -231,6 +234,7 @@ some changes made to your plugin break this configuration model.
|
||||||
### Backward compatibility test
|
### 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
|
```yaml
|
||||||
@Test public void should_be_backward_compatible() throws Exception {
|
@Test public void should_be_backward_compatible() throws Exception {
|
||||||
ConfigurationAsCode.get().configure(ConfigAsCodeTest.class.getResource("obsolete-configuration-as-code.yml").toString());
|
ConfigurationAsCode.get().configure(ConfigAsCodeTest.class.getResource("obsolete-configuration-as-code.yml").toString());
|
||||||
|
|
@ -239,6 +243,7 @@ About the later, in case you need to introduce some breaking changes, you can de
|
||||||
```
|
```
|
||||||
|
|
||||||
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:
|
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
|
```yaml
|
||||||
configuration-as-code:
|
configuration-as-code:
|
||||||
deprecated: warn
|
deprecated: warn
|
||||||
|
|
@ -253,9 +258,9 @@ You also can write a test case to check export from a live instance is well supp
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Test public void export_configuration() throws Exception {
|
@Test public void export_configuration() throws Exception {
|
||||||
/* Setup jenkins to use plugin */
|
/* Setup Jenkins to use plugin */
|
||||||
ConfigurationAsCode.get().export(System.out);
|
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.
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,21 @@
|
||||||
|
|
||||||
## Alpha Version
|
## Alpha Version
|
||||||
|
|
||||||
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.
|
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:
|
### Features available:
|
||||||
|
|
||||||
- Configure (some) plugins via yaml file
|
- Configure (some) plugins via YAML file
|
||||||
- Reload configuration from yaml file - manual step, available under `Manage Jenkins`:arrow_right: `Configuration as Code`
|
- Reload configuration from YAML file - manual step, available under `Manage Jenkins`:arrow_right: `Configuration as Code`
|
||||||
- Vault support - details in [README](../README.md)
|
- Vault support - details in [README](../README.md)
|
||||||
- seed job creation - details in [README](../README.md)
|
- seed job creation - details in [README](../README.md)
|
||||||
- build agents configuration
|
- build agents configuration
|
||||||
- github OAuth support
|
- GitHub OAuth support
|
||||||
- Matrix (and Matrix Project) Authorization Strategy support
|
- Matrix (and Matrix Project) Authorization Strategy support
|
||||||
|
|
||||||
### Limitations:
|
### 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
|
- 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)
|
- plugins has to be installed manually (and it must happen before you try to configure them)
|
||||||
|
|
||||||
### How to use it
|
### How to use it
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ API, but still require plugins to respect some contract, aka "_convention over e
|
||||||
This documentation is here to explain plugin maintainers those conventions and provide guidance
|
This documentation is here to explain plugin maintainers those conventions and provide guidance
|
||||||
on expected design.
|
on expected design.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
|
@ -20,13 +20,13 @@ Surprisingly, this is well adopted by most plugins for `Describable` components,
|
||||||
many cases 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.
|
is attached to a Descriptors.
|
||||||
|
|
||||||
## Check-list
|
## Check List
|
||||||
|
|
||||||
### Rule 1: don't write code for data-binding
|
### Rule 1: don't write code for data-binding
|
||||||
|
|
||||||
Check implementation of `Descriptor#configure(StaplerRequest,JSONObject)` in your descriptors.
|
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
|
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
|
field. Prefer exposing JavaBean setter methods, and use `req.bindJSON(this,JSONObject)` to rely
|
||||||
on introspection-friendly data-binding.
|
on introspection-friendly data-binding.
|
||||||
|
|
||||||
Within a Descriptor such setters don't have to be annotated as `@DataBoundSetter` but we suggest
|
Within a Descriptor such setters don't have to be annotated as `@DataBoundSetter` but we suggest
|
||||||
|
|
@ -70,7 +70,7 @@ public void setReplyToAddress(String address) {
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- You also need matching getters for jelly view to render current value, but you probably already have them declared.
|
- 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
|
- Use of `BulkChange` allows avoiding repeated calls to `save()` to actually persist to disk only once fully
|
||||||
configured.
|
configured.
|
||||||
- You might not even need to implement `configure` once [#3669](https://github.com/jenkinsci/jenkins/pull/3669)
|
- You might not even need to implement `configure` once [#3669](https://github.com/jenkinsci/jenkins/pull/3669)
|
||||||
is merged.
|
is merged.
|
||||||
|
|
@ -130,7 +130,7 @@ by step migration guide.
|
||||||
|
|
||||||
Checking support for JCasC is easy as long as your plugin requires 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
|
You just need the Configuration as Code plugin as a test dependency and a sample YAML file for your component
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ Jenkins Configuration as Code solves both problems - you don't need to access Je
|
||||||
|
|
||||||
## Create jenkins.yaml from scratch
|
## Create jenkins.yaml from scratch
|
||||||
|
|
||||||
We've decided to use yaml format so writing the configuration "by hand" should be easy. Your existing Jenkins can be also used as a documentation - yaml file tries to mimic UI you're used to as much as possible.
|
We've decided to use the YAML format so writing the configuration "by hand" should be easy. Your existing Jenkins can be also used as a documentation - the YAML file tries to mimic the UI you're used to as much as possible.
|
||||||
|
|
||||||
Plugin provides documentation generated for your specific Jenkins instance - after you install it, and it is available at:
|
Plugin provides documentation generated for your specific Jenkins instance - after you install it, and it is available at:
|
||||||
http://[your_jenkins_url]/configuration-as-code/
|
http://[your_jenkins_url]/configuration-as-code/
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ Configuration is not just about setting up Jenkins master, it's also about creat
|
||||||
For this purpose, we delegate to the popular [job-dsl-plugin](https://wiki.jenkins.io/display/JENKINS/Job+DSL+Plugin)
|
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.
|
and run a job-dsl script to create an initial set of jobs.
|
||||||
|
|
||||||
Be aware that you also need [configuration-as-code-support](https://plugins.jenkins.io/configuration-as-code-support) besides [configuration-as-code](https://plugins.jenkins.io/configuration-as-code) plugin already installed, otherwise the `jobs` root element cannot be parsed.
|
Be aware that you also need [Configuration as Code Support](https://plugins.jenkins.io/configuration-as-code-support) besides [Configuration as Code](https://plugins.jenkins.io/configuration-as-code) plugin already installed, otherwise the `jobs` root element cannot be parsed.
|
||||||
|
|
||||||
Typical usage is to rely on a multi-branch, or organization folder job type, so further jobs will be dynamically
|
Typical usage is to rely on a multi-branch, or organization folder job type, so further jobs will be dynamically
|
||||||
created. So a multi-branch seed job will prepare a master to be fully configured for CI/CD targeting a repository
|
created. So a multi-branch seed job will prepare a master to be fully configured for CI/CD targeting a repository
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ public class ConfigurationAsCodeTest {
|
||||||
@Test
|
@Test
|
||||||
@ConfiguredWithCode(value = {"merge1.yml", "merge3.yml"}, expected = ConfiguratorException.class)
|
@ConfiguredWithCode(value = {"merge1.yml", "merge3.yml"}, expected = ConfiguratorException.class)
|
||||||
public void shouldMergeYamlConfig() {
|
public void shouldMergeYamlConfig() {
|
||||||
assertEquals("Configured by configuration-as-code-plugin", j.jenkins.getSystemMessage());
|
assertEquals("Configured by Configuration as Code plugin", j.jenkins.getSystemMessage());
|
||||||
assertEquals(0, j.jenkins.getNumExecutors());
|
assertEquals(0, j.jenkins.getNumExecutors());
|
||||||
assertNotNull(j.jenkins.getNode("agent1"));
|
assertNotNull(j.jenkins.getNode("agent1"));
|
||||||
assertNotNull(j.jenkins.getNode("agent3"));
|
assertNotNull(j.jenkins.getNode("agent3"));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
jenkins:
|
jenkins:
|
||||||
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code Plugin\n\n"
|
systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code plugin\n\n"
|
||||||
numExecutors: 5
|
numExecutors: 5
|
||||||
mode: NORMAL
|
mode: NORMAL
|
||||||
scmCheckoutRetryCount: 4
|
scmCheckoutRetryCount: 4
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
jenkins:
|
jenkins:
|
||||||
systemMessage: "Configured by configuration-as-code-plugin"
|
systemMessage: "Configured by Configuration as Code plugin"
|
||||||
|
|
||||||
nodes:
|
nodes:
|
||||||
- dumb:
|
- dumb:
|
||||||
mode: NORMAL
|
mode: NORMAL
|
||||||
name: "agent3"
|
name: "agent3"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue