github.com/argoproj/argo-cd/v2@v2.10.9/docs/operator-manual/config-management-plugins.md (about)

     1  
     2  # Config Management Plugins
     3  
     4  Argo CD's "native" config management tools are Helm, Jsonnet, and Kustomize. If you want to use a different config
     5  management tools, or if Argo CD's native tool support does not include a feature you need, you might need to turn to
     6  a Config Management Plugin (CMP).
     7  
     8  The Argo CD "repo server" component is in charge of building Kubernetes manifests based on some source files from a
     9  Helm, OCI, or git repository. When a config management plugin is correctly configured, the repo server may delegate the
    10  task of building manifests to the plugin.
    11  
    12  The following sections will describe how to create, install, and use plugins. Check out the
    13  [example plugins](https://github.com/argoproj/argo-cd/tree/master/examples/plugins) for additional guidance.
    14  
    15  !!! warning
    16      Plugins are granted a level of trust in the Argo CD system, so it is important to implement plugins securely. Argo
    17      CD administrators should only install plugins from trusted sources, and they should audit plugins to weigh their
    18      particular risks and benefits.
    19  
    20  ## Installing a config management plugin
    21  
    22  ### Sidecar plugin
    23  
    24  An operator can configure a plugin tool via a sidecar to repo-server. The following changes are required to configure a new plugin:
    25  
    26  #### Write the plugin configuration file
    27  
    28  Plugins will be configured via a ConfigManagementPlugin manifest located inside the plugin container.
    29  
    30  ```yaml
    31  apiVersion: argoproj.io/v1alpha1
    32  kind: ConfigManagementPlugin
    33  metadata:
    34    # The name of the plugin must be unique within a given Argo CD instance.
    35    name: my-plugin
    36  spec:
    37    # The version of your plugin. Optional. If specified, the Application's spec.source.plugin.name field
    38    # must be <plugin name>-<plugin version>.
    39    version: v1.0
    40    # The init command runs in the Application source directory at the beginning of each manifest generation. The init
    41    # command can output anything. A non-zero status code will fail manifest generation.
    42    init:
    43      # Init always happens immediately before generate, but its output is not treated as manifests.
    44      # This is a good place to, for example, download chart dependencies.
    45      command: [sh]
    46      args: [-c, 'echo "Initializing..."']
    47    # The generate command runs in the Application source directory each time manifests are generated. Standard output
    48    # must be ONLY valid Kubernetes Objects in either YAML or JSON. A non-zero exit code will fail manifest generation.
    49    # To write log messages from the command, write them to stderr, it will always be displayed.
    50    # Error output will be sent to the UI, so avoid printing sensitive information (such as secrets).
    51    generate:
    52      command: [sh, -c]
    53      args:
    54        - |
    55          echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$ARGOCD_ENV_FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"
    56    # The discovery config is applied to a repository. If every configured discovery tool matches, then the plugin may be
    57    # used to generate manifests for Applications using the repository. If the discovery config is omitted then the plugin 
    58    # will not match any application but can still be invoked explicitly by specifying the plugin name in the app spec. 
    59    # Only one of fileName, find.glob, or find.command should be specified. If multiple are specified then only the 
    60    # first (in that order) is evaluated.
    61    discover:
    62      # fileName is a glob pattern (https://pkg.go.dev/path/filepath#Glob) that is applied to the Application's source 
    63      # directory. If there is a match, this plugin may be used for the Application.
    64      fileName: "./subdir/s*.yaml"
    65      find:
    66        # This does the same thing as fileName, but it supports double-start (nested directory) glob patterns.
    67        glob: "**/Chart.yaml"
    68        # The find command runs in the repository's root directory. To match, it must exit with status code 0 _and_ 
    69        # produce non-empty output to standard out.
    70        command: [sh, -c, find . -name env.yaml]
    71    # The parameters config describes what parameters the UI should display for an Application. It is up to the user to
    72    # actually set parameters in the Application manifest (in spec.source.plugin.parameters). The announcements _only_
    73    # inform the "Parameters" tab in the App Details page of the UI.
    74    parameters:
    75      # Static parameter announcements are sent to the UI for _all_ Applications handled by this plugin.
    76      # Think of the `string`, `array`, and `map` values set here as "defaults". It is up to the plugin author to make 
    77      # sure that these default values actually reflect the plugin's behavior if the user doesn't explicitly set different
    78      # values for those parameters.
    79      static:
    80        - name: string-param
    81          title: Description of the string param
    82          tooltip: Tooltip shown when the user hovers the
    83          # If this field is set, the UI will indicate to the user that they must set the value.
    84          required: false
    85          # itemType tells the UI how to present the parameter's value (or, for arrays and maps, values). Default is
    86          # "string". Examples of other types which may be supported in the future are "boolean" or "number".
    87          # Even if the itemType is not "string", the parameter value from the Application spec will be sent to the plugin
    88          # as a string. It's up to the plugin to do the appropriate conversion.
    89          itemType: ""
    90          # collectionType describes what type of value this parameter accepts (string, array, or map) and allows the UI
    91          # to present a form to match that type. Default is "string". This field must be present for non-string types.
    92          # It will not be inferred from the presence of an `array` or `map` field.
    93          collectionType: ""
    94          # This field communicates the parameter's default value to the UI. Setting this field is optional.
    95          string: default-string-value
    96        # All the fields above besides "string" apply to both the array and map type parameter announcements.
    97        - name: array-param
    98          # This field communicates the parameter's default value to the UI. Setting this field is optional.
    99          array: [default, items]
   100          collectionType: array
   101        - name: map-param
   102          # This field communicates the parameter's default value to the UI. Setting this field is optional.
   103          map:
   104            some: value
   105          collectionType: map
   106      # Dynamic parameter announcements are announcements specific to an Application handled by this plugin. For example,
   107      # the values for a Helm chart's values.yaml file could be sent as parameter announcements.
   108      dynamic:
   109        # The command is run in an Application's source directory. Standard output must be JSON matching the schema of the
   110        # static parameter announcements list.
   111        command: [echo, '[{"name": "example-param", "string": "default-string-value"}]']
   112  
   113    # If set to `true` then the plugin receives repository files with original file mode. Dangerous since the repository
   114    # might have executable files. Set to true only if you trust the CMP plugin authors.
   115    preserveFileMode: false
   116  ```
   117  
   118  !!! note
   119      While the ConfigManagementPlugin _looks like_ a Kubernetes object, it is not actually a custom resource. 
   120      It only follows kubernetes-style spec conventions.
   121  
   122  The `generate` command must print a valid Kubernetes YAML or JSON object stream to stdout. Both `init` and `generate` commands are executed inside the application source directory.
   123  
   124  The `discover.fileName` is used as [glob](https://pkg.go.dev/path/filepath#Glob) pattern to determine whether an
   125  application repository is supported by the plugin or not. 
   126  
   127  ```yaml
   128    discover:
   129      find:
   130        command: [sh, -c, find . -name env.yaml]
   131  ```
   132  
   133  If `discover.fileName` is not provided, the `discover.find.command` is executed in order to determine whether an
   134  application repository is supported by the plugin or not. The `find` command should return a non-error exit code
   135  and produce output to stdout when the application source type is supported.
   136  
   137  #### Place the plugin configuration file in the sidecar
   138  
   139  Argo CD expects the plugin configuration file to be located at `/home/argocd/cmp-server/config/plugin.yaml` in the sidecar.
   140  
   141  If you use a custom image for the sidecar, you can add the file directly to that image.
   142  
   143  ```dockerfile
   144  WORKDIR /home/argocd/cmp-server/config/
   145  COPY plugin.yaml ./
   146  ```
   147  
   148  If you use a stock image for the sidecar or would rather maintain the plugin configuration in a ConfigMap, just nest the
   149  plugin config file in a ConfigMap under the `plugin.yaml` key and mount the ConfigMap in the sidecar (see next section).
   150  
   151  ```yaml
   152  apiVersion: v1
   153  kind: ConfigMap
   154  metadata:
   155    name: my-plugin-config
   156  data:
   157    plugin.yaml: |
   158      apiVersion: argoproj.io/v1alpha1
   159      kind: ConfigManagementPlugin
   160      metadata:
   161        name: my-plugin
   162      spec:
   163        version: v1.0
   164        init:
   165          command: [sh, -c, 'echo "Initializing..."']
   166        generate:
   167          command: [sh, -c, 'echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$ARGOCD_ENV_FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"']
   168        discover:
   169          fileName: "./subdir/s*.yaml"
   170  ```
   171  
   172  #### Register the plugin sidecar
   173  
   174  To install a plugin, patch argocd-repo-server to run the plugin container as a sidecar, with argocd-cmp-server as its 
   175  entrypoint. You can use either off-the-shelf or custom-built plugin image as sidecar image. For example:
   176  
   177  ```yaml
   178  containers:
   179  - name: my-plugin
   180    command: [/var/run/argocd/argocd-cmp-server] # Entrypoint should be Argo CD lightweight CMP server i.e. argocd-cmp-server
   181    image: busybox # This can be off-the-shelf or custom-built image
   182    securityContext:
   183      runAsNonRoot: true
   184      runAsUser: 999
   185    volumeMounts:
   186      - mountPath: /var/run/argocd
   187        name: var-files
   188      - mountPath: /home/argocd/cmp-server/plugins
   189        name: plugins
   190      # Remove this volumeMount if you've chosen to bake the config file into the sidecar image.
   191      - mountPath: /home/argocd/cmp-server/config/plugin.yaml
   192        subPath: plugin.yaml
   193        name: my-plugin-config
   194      # Starting with v2.4, do NOT mount the same tmp volume as the repo-server container. The filesystem separation helps 
   195      # mitigate path traversal attacks.
   196      - mountPath: /tmp
   197        name: cmp-tmp
   198  volumes:
   199  - configMap:
   200      name: my-plugin-config
   201    name: my-plugin-config
   202  - emptyDir: {}
   203    name: cmp-tmp
   204  ``` 
   205  
   206  !!! important "Double-check these items"
   207      1. Make sure to use `/var/run/argocd/argocd-cmp-server` as an entrypoint. The `argocd-cmp-server` is a lightweight GRPC service that allows Argo CD to interact with the plugin.
   208      2. Make sure that sidecar container is running as user 999.
   209      3. Make sure that plugin configuration file is present at `/home/argocd/cmp-server/config/plugin.yaml`. It can either be volume mapped via configmap or baked into image.
   210  
   211  ### Using environment variables in your plugin
   212  
   213  Plugin commands have access to
   214  
   215  1. The system environment variables of the sidecar
   216  2. [Standard build environment variables](../user-guide/build-environment.md)
   217  3. Variables in the Application spec (References to system and build variables will get interpolated in the variables' values):
   218  
   219          apiVersion: argoproj.io/v1alpha1
   220          kind: Application
   221          spec:
   222            source:
   223              plugin:
   224                env:
   225                  - name: FOO
   226                    value: bar
   227                  - name: REV
   228                    value: test-$ARGOCD_APP_REVISION
   229      
   230      Before reaching the `init.command`, `generate.command`, and `discover.find.command` commands, Argo CD prefixes all 
   231      user-supplied environment variables (#3 above) with `ARGOCD_ENV_`. This prevents users from directly setting 
   232      potentially-sensitive environment variables.
   233  
   234  4. Parameters in the Application spec:
   235  
   236          apiVersion: argoproj.io/v1alpha1
   237          kind: Application
   238          spec:
   239           source:
   240             plugin:
   241               parameters:
   242                 - name: values-files
   243                   array: [values-dev.yaml]
   244                 - name: helm-parameters
   245                   map:
   246                     image.tag: v1.2.3
   247     
   248      The parameters are available as JSON in the `ARGOCD_APP_PARAMETERS` environment variable. The example above would
   249      produce this JSON:
   250     
   251          [{"name": "values-files", "array": ["values-dev.yaml"]}, {"name": "helm-parameters", "map": {"image.tag": "v1.2.3"}}]
   252     
   253      !!! note
   254          Parameter announcements, even if they specify defaults, are _not_ sent to the plugin in `ARGOCD_APP_PARAMETERS`.
   255          Only parameters explicitly set in the Application spec are sent to the plugin. It is up to the plugin to apply
   256          the same defaults as the ones announced to the UI.
   257     
   258      The same parameters are also available as individual environment variables. The names of the environment variables
   259      follows this convention:
   260     
   261             - name: some-string-param
   262               string: some-string-value
   263             # PARAM_SOME_STRING_PARAM=some-string-value
   264             
   265             - name: some-array-param
   266               value: [item1, item2]
   267             # PARAM_SOME_ARRAY_PARAM_0=item1
   268             # PARAM_SOME_ARRAY_PARAM_1=item2
   269             
   270             - name: some-map-param
   271               map:
   272                 image.tag: v1.2.3
   273             # PARAM_SOME_MAP_PARAM_IMAGE_TAG=v1.2.3
   274     
   275  !!! warning "Sanitize/escape user input" 
   276      As part of Argo CD's manifest generation system, config management plugins are treated with a level of trust. Be
   277      sure to escape user input in your plugin to prevent malicious input from causing unwanted behavior.
   278  
   279  ## Using a config management plugin with an Application
   280  
   281  You may leave the `name` field
   282  empty in the `plugin` section for the plugin to be automatically matched with the Application based on its discovery rules. If you do mention the name make sure 
   283  it is either `<metadata.name>-<spec.version>` if version is mentioned in the `ConfigManagementPlugin` spec or else just `<metadata.name>`. When name is explicitly 
   284  specified only that particular plugin will be used iff its discovery pattern/command matches the provided application repo.
   285  
   286  ```yaml
   287  apiVersion: argoproj.io/v1alpha1
   288  kind: Application
   289  metadata:
   290    name: guestbook
   291    namespace: argocd
   292  spec:
   293    project: default
   294    source:
   295      repoURL: https://github.com/argoproj/argocd-example-apps.git
   296      targetRevision: HEAD
   297      path: guestbook
   298      plugin:
   299        env:
   300          - name: FOO
   301            value: bar
   302  ```
   303  
   304  If you don't need to set any environment variables, you can set an empty plugin section.
   305  
   306  ```yaml
   307      plugin: {}
   308  ```
   309  
   310  !!! important
   311      If your CMP command runs too long, the command will be killed, and the UI will show an error. The CMP server
   312      respects the timeouts set by the `server.repo.server.timeout.seconds` and `controller.repo.server.timeout.seconds` 
   313      items in `argocd-cm`. Increase their values from the default of 60s.
   314  
   315      Each CMP command will also independently timeout on the `ARGOCD_EXEC_TIMEOUT` set for the CMP sidecar. The default
   316      is 90s. So if you increase the repo server timeout greater than 90s, be sure to set `ARGOCD_EXEC_TIMEOUT` on the
   317      sidecar.
   318      
   319  !!! note
   320      Each Application can only have one config management plugin configured at a time. If you're converting an existing
   321      plugin configured through the `argocd-cm` ConfigMap to a sidecar, make sure to update the plugin name to either `<metadata.name>-<spec.version>` 
   322      if version was mentioned in the `ConfigManagementPlugin` spec or else just use `<metadata.name>`. You can also remove the name altogether 
   323      and let the automatic discovery to identify the plugin.
   324  !!! note
   325      If a CMP renders blank manfiests, and `prune` is set to `true`, Argo CD will automatically remove resources. CMP plugin authors should ensure errors are part of the exit code. Commonly something like `kustomize build . | cat` won't pass errors because of the pipe. Consider setting `set -o pipefail` so anything piped will pass errors on failure.
   326  
   327  ## Debugging a CMP
   328  
   329  If you are actively developing a sidecar-installed CMP, keep a few things in mind:
   330  
   331  1. If you are mounting plugin.yaml from a ConfigMap, you will have to restart the repo-server Pod so the plugin will
   332     pick up the changes.
   333  2. If you have baked plugin.yaml into your image, you will have to build, push, and force a re-pull of that image on the
   334     repo-server Pod so the plugin will pick up the changes. If you are using `:latest`, the Pod will always pull the new
   335     image. If you're using a different, static tag, set `imagePullPolicy: Always` on the CMP's sidecar container.
   336  3. CMP errors are cached by the repo-server in Redis. Restarting the repo-server Pod will not clear the cache. Always
   337     do a "Hard Refresh" when actively developing a CMP so you have the latest output.
   338  4. Verify your sidecar has started properly by viewing the Pod and seeing that two containers are running `kubectl get pod -l app.kubernetes.io/component=repo-server -n argocd`
   339  5. Write log message to stderr and set the `--loglevel=info` flag in the sidecar. This will print everything written to stderr, even on successfull command execution.
   340  
   341  
   342  ### Other Common Errors
   343  | Error Message | Cause |
   344  | -- | -- |
   345  | `no matches for kind "ConfigManagementPlugin" in version "argoproj.io/v1alpha1"` | The `ConfigManagementPlugin` CRD was deprecated in Argo CD 2.4 and removed in 2.8. This error means you've tried to put the configuration for your plugin directly into Kubernetes as a CRD. Refer to this [section of documentation](#write-the-plugin-configuration-file) for how to write the plugin configuration file and place it properly in the sidecar. |
   346  
   347  ## Plugin tar stream exclusions
   348  
   349  In order to increase the speed of manifest generation, certain files and folders can be excluded from being sent to your
   350  plugin. We recommend excluding your `.git` folder if it isn't necessary. Use Go's
   351  [filepatch.Match](https://pkg.go.dev/path/filepath#Match) syntax. For example, `.git/*` to exclude `.git` folder.
   352  
   353  You can set it one of three ways:
   354  
   355  1. The `--plugin-tar-exclude` argument on the repo server.
   356  2. The `reposerver.plugin.tar.exclusions` key if you are using `argocd-cmd-params-cm`
   357  3. Directly setting `ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS` environment variable on the repo server.
   358  
   359  For option 1, the flag can be repeated multiple times. For option 2 and 3, you can specify multiple globs by separating
   360  them with semicolons.
   361  
   362  ## Migrating from argocd-cm plugins
   363  
   364  Installing plugins by modifying the argocd-cm ConfigMap is deprecated as of v2.4 and has been completely removed starting in v2.8.
   365  
   366  CMP plugins work by adding a sidecar to `argocd-repo-server` along with a configuration in that sidecar located at `/home/argocd/cmp-server/config/plugin.yaml`. A argocd-cm plugin can be easily converted with the following steps.
   367  
   368  ### Convert the ConfigMap entry into a config file
   369  
   370  First, copy the plugin's configuration into its own YAML file. Take for example the following ConfigMap entry:
   371  
   372  ```yaml
   373  data:
   374    configManagementPlugins: |
   375      - name: pluginName
   376        init:                          # Optional command to initialize application source directory
   377          command: ["sample command"]
   378          args: ["sample args"]
   379        generate:                      # Command to generate Kubernetes Objects in either YAML or JSON
   380          command: ["sample command"]
   381          args: ["sample args"]
   382        lockRepo: true                 # Defaults to false. See below.
   383  ```
   384  
   385  The `pluginName` item would be converted to a config file like this:
   386  
   387  ```yaml
   388  apiVersion: argoproj.io/v1alpha1
   389  kind: ConfigManagementPlugin
   390  metadata:
   391    name: pluginName
   392  spec:
   393    init:                          # Optional command to initialize application source directory
   394      command: ["sample command"]
   395      args: ["sample args"]
   396    generate:                      # Command to generate Kubernetes Objects in either YAML or JSON
   397      command: ["sample command"]
   398      args: ["sample args"]
   399  ```
   400  
   401  !!! note
   402      The `lockRepo` key is not relevant for sidecar plugins, because sidecar plugins do not share a single source repo
   403      directory when generating manifests.
   404  
   405  Next, we need to decide how this yaml is going to be added to the sidecar. We can either bake the yaml directly into the image, or we can mount it from a ConfigMap. 
   406  
   407  If using a ConfigMap, our example would look like this:
   408  
   409  ```yaml
   410  apiVersion: v1
   411  kind: ConfigMap
   412  metadata:
   413    name: pluginName
   414    namespace: argocd
   415  data:
   416    pluginName.yaml: |
   417      apiVersion: argoproj.io/v1alpha1
   418      kind: ConfigManagementPlugin
   419      metadata:
   420        name: pluginName
   421      spec:
   422        init:                          # Optional command to initialize application source directory
   423          command: ["sample command"]
   424          args: ["sample args"]
   425        generate:                      # Command to generate Kubernetes Objects in either YAML or JSON
   426          command: ["sample command"]
   427          args: ["sample args"]
   428  ```
   429  
   430  Then this would be mounted in our plugin sidecar.
   431  
   432  ### Write discovery rules for your plugin
   433  
   434  Sidecar plugins can use either discovery rules or a plugin name to match Applications to plugins. If the discovery rule is omitted 
   435  then you have to explicitly specify the plugin by name in the app spec or else that particular plugin will not match any app.
   436  
   437  If you want to use discovery instead of the plugin name to match applications to your plugin, write rules applicable to 
   438  your plugin [using the instructions above](#1-write-the-plugin-configuration-file) and add them to your configuration 
   439  file.
   440  
   441  To use the name instead of discovery, update the name in your application manifest to `<metadata.name>-<spec.version>` 
   442  if version was mentioned in the `ConfigManagementPlugin` spec or else just use `<metadata.name>`. For example:
   443  
   444  ```yaml
   445  apiVersion: argoproj.io/v1alpha1
   446  kind: Application
   447  metadata:
   448    name: guestbook
   449  spec:
   450    source:
   451      plugin:
   452        name: pluginName  # Delete this for auto-discovery (and set `plugin: {}` if `name` was the only value) or use proper sidecar plugin name
   453  ```
   454  
   455  ### Make sure the plugin has access to the tools it needs
   456  
   457  Plugins configured with argocd-cm ran on the Argo CD image. This gave it access to all the tools installed on that
   458  image by default (see the [Dockerfile](https://github.com/argoproj/argo-cd/blob/master/Dockerfile) for base image and
   459  installed tools).
   460  
   461  You can either use a stock image (like busybox, or alpine/k8s) or design your own base image with the tools your plugin needs. For
   462  security, avoid using images with more binaries installed than what your plugin actually needs.
   463  
   464  ### Test the plugin
   465  
   466  After installing the plugin as a sidecar [according to the directions above](#installing-a-config-management-plugin),
   467  test it out on a few Applications before migrating all of them to the sidecar plugin.
   468  
   469  Once tests have checked out, remove the plugin entry from your argocd-cm ConfigMap.
   470  
   471  ### Additional Settings
   472  
   473  #### Preserve repository files mode
   474  
   475  By default, config management plugin receives source repository files with reset file mode. This is done for security
   476  reasons. If you want to preserve original file mode, you can set `preserveFileMode` to `true` in the plugin spec:
   477  
   478  !!! warning
   479      Make sure you trust the plugin you are using. If you set `preserveFileMode` to `true` then the plugin might receive
   480      files with executable permissions which can be a security risk.
   481  
   482  ```yaml
   483  apiVersion: argoproj.io/v1alpha1
   484  kind: ConfigManagementPlugin
   485  metadata:
   486    name: pluginName
   487  spec:
   488    init:
   489      command: ["sample command"]
   490      args: ["sample args"]
   491    generate:
   492      command: ["sample command"]
   493      args: ["sample args"]
   494    preserveFileMode: true
   495  ```