From d42f6da13df1fbafcc0d3e11b72c9ee6e1b7a4af Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 13 May 2026 17:19:02 +0200 Subject: [PATCH 1/7] Capability to create Kubernetes client with k8s.io/client-go --- internal/k8s/client.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 internal/k8s/client.go diff --git a/internal/k8s/client.go b/internal/k8s/client.go new file mode 100644 index 0000000..ba9a395 --- /dev/null +++ b/internal/k8s/client.go @@ -0,0 +1,27 @@ +package k8s + +import ( + "fmt" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +func NewClient(kubeContext string) (kubernetes.Interface, error) { + clientConfigLoader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), + &clientcmd.ConfigOverrides{CurrentContext: kubeContext}, + ) + + restConfig, err := clientConfigLoader.ClientConfig() + if err != nil { + return nil, fmt.Errorf("failed to build kubernetes client config: %w", err) + } + + client, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return nil, fmt.Errorf("failed to create kubernetes client: %w", err) + } + + return client, nil +} From 0ef817c7e92194d2e92e62b9547dfc7520e8d7b6 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 13 May 2026 15:46:27 +0200 Subject: [PATCH 2/7] go.mod & go.sum --- go.mod | 38 +++++++++++++++++++------- go.sum | 84 +++++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 98 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 42988d1..7dece06 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/stackrox/roxie -go 1.26 +go 1.26.0 require ( dario.cat/mergo v1.0.2 @@ -11,14 +11,34 @@ require ( github.com/stretchr/testify v1.11.1 golang.org/x/term v0.42.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apimachinery v0.35.3 - k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 + k8s.io/apimachinery v0.36.1 + k8s.io/client-go v0.36.1 + k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 +) + +require ( + github.com/emicklei/go-restful/v3 v3.13.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/google/gnostic-models v0.7.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect + k8s.io/api v0.36.1 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) require ( github.com/Masterminds/semver/v3 v3.5.0 github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/cli v29.4.0+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.3 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect @@ -33,20 +53,20 @@ require ( github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/vbatts/tar-split v0.12.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/net v0.48.0 // indirect + golang.org/x/net v0.49.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.43.0 // indirect golang.org/x/text v0.35.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gotest.tools/v3 v3.5.2 // indirect - k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/klog/v2 v2.140.0 // indirect + k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect ) diff --git a/go.sum b/go.sum index 52f7248..9c1ec9b 100644 --- a/go.sum +++ b/go.sum @@ -5,30 +5,57 @@ github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lpr github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw= github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/cli v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM= github.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.21.5 h1:KTJG9Pn/jC0VdZR6ctV3/jcN+q6/Iqlx0sTVz3ywZlM= github.com/google/go-containerregistry v0.21.5/go.mod h1:ySvMuiWg+dOsRW0Hw8GYwfMwBlNRTmpYBFJPlkco5zU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -41,12 +68,17 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= @@ -56,7 +88,14 @@ github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= @@ -65,9 +104,12 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -77,27 +119,39 @@ golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= -k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/api v0.36.1 h1:XbL/EMj8K2aJpJtePmqUyQMsM0D4QI2pvl7YKJ20FTY= +k8s.io/api v0.36.1/go.mod h1:KOWo4ey3TINlXjeHVuwB3i+tXXnu+UcwFBHlI/9dvEo= +k8s.io/apimachinery v0.36.1 h1:G63Gjx2W+q0YD+72Vo8oY0nDnePVwnuzTmmy5ENrVSA= +k8s.io/apimachinery v0.36.1/go.mod h1:ibYOR00vW/I1kzvi5SF0dRuJ52BvKtfvRdOn35GPQ+8= +k8s.io/client-go v0.36.1 h1:FN/K8QIT2CEDt+2WB2HnWrUANZ50AP5GII43/SP2JR0= +k8s.io/client-go v0.36.1/go.mod h1:s6rAnCtTGYDQnpNjEhSaISV+2O8jwruZ6m3QOYBFbtU= +k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= +k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= +k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg= +k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= From 19aff82ae0b57a9076c97a12e7361a0539c64c9e Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 13 May 2026 17:26:09 +0200 Subject: [PATCH 3/7] Create kubernetes client as part of the Deployer --- internal/deployer/deployer.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index 44565b8..7d31cdc 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -13,6 +13,7 @@ import ( "time" "github.com/fatih/color" + "k8s.io/client-go/kubernetes" "github.com/stackrox/roxie/internal/component" "github.com/stackrox/roxie/internal/dockerauth" @@ -46,6 +47,7 @@ type Deployer struct { envrcFile string kubeContext string + k8sClient kubernetes.Interface config Config @@ -263,6 +265,18 @@ func New(log *logger.Logger) (*Deployer, error) { d.kubeContext = env.GetCurrentContext() + // Created eagerly (not lazily on first use) because + // 1. we expect to make more extensive use of it + // 2. we need a working connection to the API server anyway. + if d.kubeContext != "" { + client, err := k8s.NewClient(d.kubeContext) + if err != nil { + return nil, fmt.Errorf("creating new Kubernetes client: %w", err) + } + d.k8sClient = client + + } + log.Success("🚀 ACS Deployer initialized") return d, nil From bc2bd999d2087614da7f24dee4a17248429c82b5 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 13 May 2026 17:46:51 +0200 Subject: [PATCH 4/7] Implementation of pull secret injection --- internal/deployer/openshift.go | 125 +++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 internal/deployer/openshift.go diff --git a/internal/deployer/openshift.go b/internal/deployer/openshift.go new file mode 100644 index 0000000..22b6ae4 --- /dev/null +++ b/internal/deployer/openshift.go @@ -0,0 +1,125 @@ +package deployer + +import ( + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + + "github.com/stackrox/roxie/internal/dockerauth" + v1 "k8s.io/api/core/v1" + k8sapierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/util/retry" +) + +const ( + openshiftConfigNamespace = "openshift-config" + openshiftGlobalPullSecretName = "pull-secret" + dockerConfigJsonKey = ".dockerconfigjson" + registryForDownstreamImages = "quay.io/rhacs-eng" +) + +var ( + namespacedGlobalPullSecret = openshiftConfigNamespace + "/" + openshiftGlobalPullSecretName +) + +// dockerConfigJSON represents the structure of a .dockerconfigjson secret value. +type dockerConfigJSON struct { + Auths map[string]dockerauth.AuthEntry `json:"auths"` +} + +// InjectGlobalOpenShiftPullSecret adds registry credentials to the OpenShift global pull secret. +func (d *Deployer) InjectGlobalOpenShiftPullSecret(ctx context.Context) error { + // Retry on Conflict, AlreadyExists, and NotFound to handle TOCTOU races between + // the Get and subsequent Create/Update (e.g., secret deleted after Get -> Update + // returns NotFound; secret created by another caller after Get -> Create returns + // AlreadyExists). + return retry.OnError(retry.DefaultRetry, func(err error) bool { + return k8sapierrors.IsConflict(err) || k8sapierrors.IsAlreadyExists(err) || k8sapierrors.IsNotFound(err) + }, func() error { + return d.injectGlobalOpenShiftPullSecretOnce(ctx) + }) +} + +func (d *Deployer) injectGlobalOpenShiftPullSecretOnce(ctx context.Context) error { + if d.dockerCreds == nil { + return errors.New("no pull secrets found") + } + credentials := *d.dockerCreds + + if d.k8sClient == nil { + return errors.New("k8s client not initialized") + } + + secrets := d.k8sClient.CoreV1().Secrets(openshiftConfigNamespace) + secret, err := secrets.Get(ctx, openshiftGlobalPullSecretName, metav1.GetOptions{}) + if err != nil { + if !k8sapierrors.IsNotFound(err) { + return fmt.Errorf("retrieving secret %s: %w", namespacedGlobalPullSecret, err) + } + secret = &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: openshiftGlobalPullSecretName, + Namespace: openshiftConfigNamespace, + }, + Type: v1.SecretTypeDockerConfigJson, + } + } + + modified, err := injectRegistryCredentialsIntoSecret(credentials, secret) + if err != nil { + return fmt.Errorf("injecting registry credentials into Kubernetes secret: %w", err) + } + if !modified { + d.logger.Dimf("Global pull secret %s already contains entry for %s, skipping", namespacedGlobalPullSecret, registryForDownstreamImages) + return nil + } + + if secret.ResourceVersion == "" { + if _, err := secrets.Create(ctx, secret, metav1.CreateOptions{}); err != nil { + return fmt.Errorf("creating secret %s: %w", namespacedGlobalPullSecret, err) + } + } else { + if _, err := secrets.Update(ctx, secret, metav1.UpdateOptions{}); err != nil { + return fmt.Errorf("updating secret %s: %w", namespacedGlobalPullSecret, err) + } + } + + d.logger.Successf("Injected pull secret for %s into %s", registryForDownstreamImages, namespacedGlobalPullSecret) + return nil +} + +// injectRegistryCredentialsIntoSecret mutates the secret in place, returning true if it was modified. +func injectRegistryCredentialsIntoSecret(credentials dockerauth.Credentials, secret *v1.Secret) (bool, error) { + var cfg dockerConfigJSON + if data, ok := secret.Data[dockerConfigJsonKey]; ok { + if err := json.Unmarshal(data, &cfg); err != nil { + return false, fmt.Errorf("unmarshaling %q in %s: %w", dockerConfigJsonKey, namespacedGlobalPullSecret, err) + } + } + if cfg.Auths == nil { + cfg.Auths = make(map[string]dockerauth.AuthEntry) + } + + if _, ok := cfg.Auths[registryForDownstreamImages]; ok { + return false, nil + } + + cfg.Auths[registryForDownstreamImages] = dockerauth.AuthEntry{ + Auth: base64.StdEncoding.EncodeToString([]byte(credentials.Username + ":" + credentials.Password)), + } + + updated, err := json.Marshal(cfg) + if err != nil { + return false, fmt.Errorf("marshaling updated docker config: %w", err) + } + + if secret.Data == nil { + secret.Data = make(map[string][]byte) + } + secret.Data[dockerConfigJsonKey] = updated + + return true, nil +} From f2e97dd8535fe6e9728b59353e17a0860c888afc Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 13 May 2026 17:23:30 +0200 Subject: [PATCH 5/7] Invoke pull secret patching before deploying --- internal/deployer/deployer.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index 7d31cdc..6407e5d 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -274,7 +274,6 @@ func New(log *logger.Logger) (*Deployer, error) { return nil, fmt.Errorf("creating new Kubernetes client: %w", err) } d.k8sClient = client - } log.Success("🚀 ACS Deployer initialized") @@ -320,6 +319,17 @@ func (d *Deployer) Deploy(ctx context.Context, components component.Component) e d.logger.Infof("Initiating deployment of %s", components) + if d.config.Roxie.KonfluxImages && d.config.Roxie.ClusterType == types.ClusterTypeOpenShift4 { + // For deploying Konflux-built images, we need to configure image-rewriting on the cluster at the CRI-O level. + // But due to https://access.redhat.com/solutions/6540591 the standard pull-secret mechanism doesn't work for the + // target image references. A workaround is to inject the pull secrets we need into OpenShift's global + // pull secrets. + // Infra OpenShift4 clusters already come equipped with this global pull secret. + if err := d.InjectGlobalOpenShiftPullSecret(ctx); err != nil { + return fmt.Errorf("injecting global OpenShift pull-secret for Konflux images: %w", err) + } + } + // If only deploying operator, use the operator-only flow. if components.IncludesOperatorExplicitly() { return d.deployOperatorOnly(ctx) From 0974da611b7abe3184484b12743fe832c8d9cd49 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 13 May 2026 18:15:54 +0200 Subject: [PATCH 6/7] Tests --- internal/deployer/openshift_test.go | 118 ++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 internal/deployer/openshift_test.go diff --git a/internal/deployer/openshift_test.go b/internal/deployer/openshift_test.go new file mode 100644 index 0000000..ae3d7b1 --- /dev/null +++ b/internal/deployer/openshift_test.go @@ -0,0 +1,118 @@ +package deployer + +import ( + "encoding/base64" + "encoding/json" + "testing" + + "github.com/stackrox/roxie/internal/dockerauth" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" +) + +func TestInjectRegistryCredentialsIntoSecret(t *testing.T) { + const ( + registryUsername = "user" + registryPassword = "pass" + ) + + makeSecret := func(credentials map[string]map[string]string) *v1.Secret { + data, err := json.Marshal(map[string]any{ + "auths": credentials, + }) + require.NoError(t, err) + return &v1.Secret{Data: map[string][]byte{dockerConfigJsonKey: data}} + } + + encodeCredentials := func(username, password string) string { + return base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) + } + + tests := []struct { + name string + secret *v1.Secret + expectModified bool + expectError bool + expectCredentials map[string]map[string]string + }{ + { + name: "injects into empty auths", + secret: makeSecret(nil), + expectModified: true, + expectCredentials: map[string]map[string]string{ + registryForDownstreamImages: { + "auth": encodeCredentials(registryUsername, registryPassword), + }, + }, + }, + { + name: "preserves existing entries", + secret: makeSecret(map[string]map[string]string{ + "registry.example.com": { + "auth": encodeCredentials("other", "secret"), + }, + }), + expectModified: true, + expectCredentials: map[string]map[string]string{ + "registry.example.com": { + "auth": encodeCredentials("other", "secret"), + }, + registryForDownstreamImages: { + "auth": encodeCredentials(registryUsername, registryPassword), + }, + }, + }, + { + name: "skips if already present", + secret: makeSecret(map[string]map[string]string{ + registryForDownstreamImages: { + "auth": encodeCredentials("existing", "existing"), + }, + }), + expectModified: false, + expectCredentials: map[string]map[string]string{ + registryForDownstreamImages: { + "auth": encodeCredentials("existing", "existing"), + }, + }, + }, + { + name: "handles nil secret data", + secret: &v1.Secret{}, + expectModified: true, + expectCredentials: map[string]map[string]string{ + registryForDownstreamImages: { + "auth": encodeCredentials(registryUsername, registryPassword), + }, + }, + }, + { + name: "returns error on invalid JSON", + secret: &v1.Secret{Data: map[string][]byte{dockerConfigJsonKey: []byte("not json")}}, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + creds := dockerauth.Credentials{Username: registryUsername, Password: registryPassword} + modified, err := injectRegistryCredentialsIntoSecret(creds, tt.secret) + if tt.expectError { + assert.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tt.expectModified, modified) + + var cfg dockerConfigJSON + require.NoError(t, json.Unmarshal(tt.secret.Data[dockerConfigJsonKey], &cfg)) + + assert.Equal(t, len(tt.expectCredentials), len(cfg.Auths), "credential length mismatch") + + for regName, regCredentials := range tt.expectCredentials { + assert.Equal(t, regCredentials["auth"], cfg.Auths[regName].Auth, "credentials mismatch for registry %s", regName) + } + }) + } +} From 07e05e247a4096b12ca0eb4c107cbac592951de0 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 20 May 2026 11:28:22 +0200 Subject: [PATCH 7/7] go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 7dece06..4d45d39 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/stretchr/testify v1.11.1 golang.org/x/term v0.42.0 gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.36.1 k8s.io/apimachinery v0.36.1 k8s.io/client-go v0.36.1 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 @@ -31,7 +32,6 @@ require ( golang.org/x/time v0.14.0 // indirect google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect - k8s.io/api v0.36.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect )