Skip to content

Commit 2a7b4f7

Browse files
committed
feat: add Argo CMP support
1 parent 8d74760 commit 2a7b4f7

File tree

25 files changed

+2380
-5
lines changed

25 files changed

+2380
-5
lines changed

.dockerignore

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Ignore files that are not needed in the Docker build context
2+
.git
3+
.gitignore
4+
Dockerfile
5+
.dockerignore
6+
README.md
7+
LICENSE
8+
# Ignore local development files
9+
*.swp
10+
*.swo
11+
# Ignore build artifacts
12+
argo-values
13+
# Ignore IDE files
14+
.idea/
15+
.vscode/
16+
# Ignore binary files that might be in the directory
17+
bin/
18+
# Ignore any temporary files
19+
tmp/
20+
*.tmp

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Go
2+
argo-values
3+
*.test
4+
*.out
5+
6+
# JetBrains IDE
7+
.idea/
8+
9+
# macOS
10+
.DS_Store
11+
12+
# Windows
13+
Thumbs.db

Dockerfile

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Build stage - compile the Go application
2+
FROM golang:1.26-alpine AS builder
3+
4+
WORKDIR /app
5+
6+
# Copy go mod and sum files
7+
COPY go.mod go.sum ./
8+
9+
# Download dependencies
10+
RUN go mod download
11+
12+
# Copy source code
13+
COPY . .
14+
15+
# Build the application
16+
RUN CGO_ENABLED=0 GOOS=linux go build -o argo-values cmd/argo-values/main.go
17+
18+
# Final stage - create a minimal image
19+
FROM alpine:latest
20+
21+
ARG HELM_VERSION=3.19.0-r5
22+
ARG APP_VERSION=1.0.0
23+
ARG GO_VERSION=1.26
24+
25+
# Add labels for better image identification
26+
LABEL org.opencontainers.image.title="argo-values"
27+
LABEL org.opencontainers.image.description="CLI tool for Argo Application values"
28+
LABEL org.opencontainers.image.version="${APP_VERSION}"
29+
LABEL org.opencontainers.image.helm.version="${HELM_VERSION}"
30+
LABEL org.opencontainers.image.go.version="${GO_VERSION}"
31+
LABEL maintainer="henning@huhehu.com"
32+
33+
# Create a non-root user for security
34+
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
35+
36+
WORKDIR /home/appuser
37+
38+
# Copy the compiled binary from the builder stage to a system directory
39+
COPY --from=builder /app/argo-values /usr/local/bin/argo-values
40+
41+
# Set proper permissions so all users can execute the binary
42+
RUN chmod +x /usr/local/bin/argo-values && \
43+
chown root:appgroup /usr/local/bin/argo-values
44+
45+
# Switch to root for package installation
46+
USER root
47+
48+
# Install Helm (required for build command)
49+
# Allow Helm version to be specified via build argument
50+
# To find available versions: https://pkgs.alpinelinux.org/packages?name=helm
51+
RUN apk add --no-cache --upgrade helm=$HELM_VERSION
52+
53+
# Switch back to non-root user for security
54+
USER appuser
55+
56+
# Set up entrypoint
57+
ENTRYPOINT ["argo-values"]
58+
59+
# Set the command to show help by default
60+
CMD ["--help"]

LICENSE

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
MIT License
22

3-
Copyright (c) 2026 HBTGmbH
3+
Copyright (c) 2026 HBT Hamburger Berater Team GmbH
44

5-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
611

7-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
814

9-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,144 @@
1-
# argo-values
1+
# Argo Values - Argo CD CMP Plugin
22

3+
[![License](https://img.shields.io/github/license/HBTGmbH/argo-values)](https://github.com/HBTGmbH/argo-values/blob/main/LICENSE)
4+
5+
Argo Values is a Config Management Plugin (CMP) for Argo CD that enhances Helm-based deployments by integrating values from Kubernetes ConfigMaps and Secrets.
6+
7+
## Features
8+
9+
- **Dynamic Value Injection**: Merge values from ConfigMaps and Secrets into Helm template application files
10+
- **Environment Variable Substitution**: Replace `${VAR_NAME}` placeholders in application files with actual values
11+
- **Automatic Refresh**: Watch for ConfigMap/Secret changes and trigger application refreshes
12+
- **Helm Integration**: Full compatibility with Helm charts and templating
13+
- **Selective Processing**: Respect `.helmignore` patterns
14+
15+
## Example
16+
17+
Install ArgoCD with CMP and an example application:
18+
```bash
19+
helm upgrade --install argocd argo/argo-cd -n argocd -f values.yaml
20+
```
21+
22+
values.yaml:
23+
```yaml
24+
25+
# add argo-values as sidecar to Argo controller for automatic application refresh
26+
controller:
27+
extraContainers:
28+
- name: argo-values
29+
image: argo-values:latest
30+
command: [argo-values]
31+
args: [watch]
32+
33+
# add argo-values as CMP to Argo's repo-server
34+
repoServer:
35+
initContainers:
36+
- name: copy-cmp-server
37+
image: quay.io/argoproj/argocd:v3.3.2
38+
command: [cp, /usr/local/bin/argocd-cmp-server, /custom-tools/]
39+
volumeMounts:
40+
- name: custom-tools
41+
mountPath: /custom-tools
42+
extraContainers:
43+
- name: argo-values
44+
image: argo-values:latest
45+
command: [/custom-tools/argocd-cmp-server]
46+
imagePullPolicy: Always
47+
securityContext:
48+
runAsNonRoot: true
49+
runAsUser: 999
50+
volumeMounts:
51+
- name: var-files
52+
mountPath: /var/run/argocd
53+
- name: plugins
54+
mountPath: /home/argocd/cmp-server/plugins
55+
- name: cmp-tmp
56+
mountPath: /tmp
57+
- name: cmp-plugin-config
58+
mountPath: /home/argocd/cmp-server/config/plugin.yaml
59+
subPath: plugin.yaml
60+
- name: custom-tools
61+
mountPath: /custom-tools
62+
volumes:
63+
- name: cmp-plugin-config
64+
configMap:
65+
name: argocd-cmp-plugin-config
66+
- name: cmp-tmp
67+
emptyDir: {}
68+
- name: custom-tools
69+
emptyDir: {}
70+
71+
# the CMP configuration
72+
extraObjects:
73+
- apiVersion: v1
74+
kind: ConfigMap
75+
metadata:
76+
name: argocd-cmp-plugin-config
77+
namespace: argocd
78+
data:
79+
plugin.yaml: |
80+
apiVersion: argoproj.io/v1alpha1
81+
kind: ConfigManagementPlugin
82+
metadata:
83+
name: argo-values
84+
spec:
85+
version: v1.0
86+
init:
87+
command: [argo-values]
88+
args: [init]
89+
generate:
90+
command: [argo-values]
91+
args: [generate]
92+
discover:
93+
find:
94+
command: [argo-values]
95+
args: [discover]
96+
97+
# an example application with two additional ConfigMaps
98+
- apiVersion: argoproj.io/v1alpha1
99+
kind: Application
100+
metadata:
101+
name: nginx-example
102+
namespace: argocd
103+
spec:
104+
project: default
105+
source:
106+
repoURL: https://github.com/argoproj/argocd-example-apps.git
107+
targetRevision: HEAD
108+
path: helm-guestbook
109+
plugin:
110+
env:
111+
- name: env-configs
112+
value: test-env
113+
- name: value-configs
114+
value: test-values
115+
destination:
116+
server: https://kubernetes.default.svc # Targets the same cluster as Argo CD
117+
namespace: default
118+
- apiVersion: v1
119+
kind: ConfigMap
120+
metadata:
121+
name: test-env
122+
namespace: default
123+
data:
124+
values.yaml: |
125+
cpu: "100m"
126+
- apiVersion: v1
127+
kind: ConfigMap
128+
metadata:
129+
name: test-values
130+
namespace: default
131+
data:
132+
values.yaml: |
133+
resources:
134+
requests:
135+
cpu: "${CPU}"
136+
```
137+
138+
## License
139+
140+
Apache License 2.0 - See [LICENSE](LICENSE) for details.
141+
142+
## Contributing
143+
144+
Contributions are welcome! Please open issues and pull requests on GitHub.

cmd/argo-values/main.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package main
2+
3+
import (
4+
"argo-values/internal/commands"
5+
"argo-values/internal/config"
6+
"argo-values/internal/logger"
7+
"os"
8+
9+
"github.com/spf13/cobra"
10+
"k8s.io/utils/env"
11+
)
12+
13+
var RootCmd = &cobra.Command{
14+
Use: "argo-values",
15+
Short: "A CLI tool for managing Argo application with values from additional resources like ConfigMaps and Secrets",
16+
Run: func(cmd *cobra.Command, args []string) {
17+
// Default behavior when no subcommand is provided
18+
_ = cmd.Help()
19+
},
20+
PersistentPreRun: func(cmd *cobra.Command, args []string) {
21+
// Initialize logger with the configured settings
22+
logger.Init(config.LogLevel, config.LogFormat, config.LogOutput)
23+
},
24+
}
25+
26+
func init() {
27+
// Add global flags
28+
RootCmd.PersistentFlags().StringVarP(&config.LogLevel, "log-level", "l", env.GetString("LOG_LEVEL", "debug"), "Log level (debug, info, warn, error, fatal, panic)")
29+
RootCmd.PersistentFlags().StringVar(&config.LogFormat, "log-format", env.GetString("LOG_FORMAT", "json"), "Log format (text, json)")
30+
RootCmd.PersistentFlags().StringVar(&config.LogOutput, "log-output", env.GetString("LOG_OUTPUT", "stdout"), "Log output (stderr, stdout)")
31+
32+
RootCmd.PersistentFlags().StringVarP(&config.KubeConfigPath, "kubeconfig", "k", "", "Path to kubeconfig file")
33+
34+
RootCmd.PersistentFlags().StringVarP(&config.ValuesFileName, "file-values", "v", "app-values.yaml", "Path to values file")
35+
RootCmd.PersistentFlags().StringVarP(&config.TargetDirectory, "file-target", "t", ".", "Path to application files")
36+
37+
RootCmd.PersistentFlags().BoolVarP(&config.DryRun, "dry-run", "d", false, "dry-run enabled (true, false)")
38+
39+
// Add subcommands
40+
RootCmd.AddCommand(commands.DiscoverCommand)
41+
RootCmd.AddCommand(commands.InitCmd)
42+
RootCmd.AddCommand(commands.GenerateCmd)
43+
RootCmd.AddCommand(commands.WatchCmd)
44+
}
45+
46+
func main() {
47+
if err := RootCmd.Execute(); err != nil {
48+
logger.Error(err.Error())
49+
os.Exit(1)
50+
}
51+
}

go.mod

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
module argo-values
2+
3+
go 1.26
4+
5+
require (
6+
github.com/sirupsen/logrus v1.9.4
7+
github.com/spf13/cobra v1.8.1
8+
gopkg.in/yaml.v3 v3.0.1
9+
k8s.io/api v0.31.2
10+
k8s.io/apimachinery v0.31.2
11+
k8s.io/client-go v0.31.2
12+
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
13+
)
14+
15+
require (
16+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
17+
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
18+
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
19+
github.com/go-logr/logr v1.4.2 // indirect
20+
github.com/go-openapi/jsonpointer v0.21.0 // indirect
21+
github.com/go-openapi/jsonreference v0.21.0 // indirect
22+
github.com/go-openapi/swag v0.23.0 // indirect
23+
github.com/gogo/protobuf v1.3.2 // indirect
24+
github.com/golang/protobuf v1.5.4 // indirect
25+
github.com/google/gnostic-models v0.6.8 // indirect
26+
github.com/google/go-cmp v0.6.0 // indirect
27+
github.com/google/gofuzz v1.2.0 // indirect
28+
github.com/google/uuid v1.6.0 // indirect
29+
github.com/imdario/mergo v0.3.16 // indirect
30+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
31+
github.com/josharian/intern v1.0.0 // indirect
32+
github.com/json-iterator/go v1.1.12 // indirect
33+
github.com/mailru/easyjson v0.7.7 // indirect
34+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
35+
github.com/modern-go/reflect2 v1.0.2 // indirect
36+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
37+
github.com/onsi/gomega v1.34.1 // indirect
38+
github.com/spf13/pflag v1.0.5 // indirect
39+
github.com/x448/float16 v0.8.4 // indirect
40+
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
41+
golang.org/x/net v0.39.0 // indirect
42+
golang.org/x/oauth2 v0.24.0 // indirect
43+
golang.org/x/sys v0.32.0 // indirect
44+
golang.org/x/term v0.31.0 // indirect
45+
golang.org/x/text v0.24.0 // indirect
46+
golang.org/x/time v0.8.0 // indirect
47+
golang.org/x/tools v0.27.0 // indirect
48+
google.golang.org/protobuf v1.35.2 // indirect
49+
gopkg.in/inf.v0 v0.9.1 // indirect
50+
gopkg.in/yaml.v2 v2.4.0 // indirect
51+
k8s.io/klog/v2 v2.130.1 // indirect
52+
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
53+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
54+
sigs.k8s.io/structured-merge-diff/v4 v4.4.4-0.20241211184406-7bf59b3d70ee // indirect
55+
sigs.k8s.io/yaml v1.4.0 // indirect
56+
)

0 commit comments

Comments
 (0)