Added support for CSI secret provider

Signed-off-by: Zanis <22601571+ZanisO@users.noreply.github.com>
This commit is contained in:
Zanis
2025-02-05 21:51:10 +00:00
parent 574129d637
commit f6887b4d8a
12 changed files with 83 additions and 5 deletions
+1
View File
@@ -69,6 +69,7 @@ require (
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/secrets-store-csi-driver v1.4.7 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
+2
View File
@@ -403,6 +403,8 @@ k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/secrets-store-csi-driver v1.4.7 h1:AyuwmPTW2GoPD2RjyVD3OrH1J9cdPZx+0h2qJvzbGXs=
sigs.k8s.io/secrets-store-csi-driver v1.4.7/go.mod h1:0/wMVOv8qLx7YNVMGU+Sh7S4D6TH6GhyEpouo28OTUU=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
+5
View File
@@ -54,6 +54,7 @@ func NewReloaderCommand() *cobra.Command {
cmd.PersistentFlags().StringVar(&options.ReloadOnDelete, "reload-on-delete", "false", "Add support to watch delete events")
cmd.PersistentFlags().BoolVar(&options.EnableHA, "enable-ha", false, "Adds support for running multiple replicas via leadership election")
cmd.PersistentFlags().BoolVar(&options.SyncAfterRestart, "sync-after-restart", false, "Sync add events after reloader restarts")
cmd.PersistentFlags().BoolVar(&options.EnableCSIIntegration, "enable-csi-integration", false, "Watch SecretProviderClassPodStatus for changes")
return cmd
}
@@ -176,6 +177,10 @@ func startReloader(cmd *cobra.Command, args []string) {
var controllers []*controller.Controller
for k := range kube.ResourceMap {
if k == "secretproviderclasspodstatuses" && !options.EnableCSIIntegration {
continue
}
if ignoredResourcesList.Contains(k) || (len(namespaceLabelSelector) == 0 && k == "namespaces") {
continue
}
+2
View File
@@ -8,6 +8,8 @@ const (
ConfigmapEnvVarPostfix = "CONFIGMAP"
// SecretEnvVarPostfix is a postfix for secret envVar
SecretEnvVarPostfix = "SECRET"
// SecretEnvVarSecretProviderClassPodStatus is a postfix for secretproviderclasspodstatus envVar
SecretProviderClassEnvVarPostfix = "SECRETPROVIDERCLASS"
// EnvVarPrefix is a Prefix for environment variable
EnvVarPrefix = "STAKATER_"
+10 -1
View File
@@ -79,7 +79,16 @@ func NewController(
}
}
listWatcher := cache.NewFilteredListWatchFromClient(client.CoreV1().RESTClient(), resource, namespace, optionsModifier)
getterRESTClient := client.CoreV1().RESTClient()
if resource == "secretproviderclasspodstatuses" {
csiClient, err := kube.GetCSIClient()
if err != nil {
logrus.Fatal(err)
}
getterRESTClient = csiClient.SecretsstoreV1().RESTClient()
}
listWatcher := cache.NewFilteredListWatchFromClient(getterRESTClient, resource, namespace, optionsModifier)
_, informer := cache.NewInformerWithOptions(cache.InformerOptions{
ListerWatcher: listWatcher,
+4
View File
@@ -7,6 +7,7 @@ import (
"github.com/stakater/Reloader/internal/pkg/util"
v1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/record"
csiv1 "sigs.k8s.io/secrets-store-csi-driver/apis/v1"
)
// ResourceUpdatedHandler contains updated objects
@@ -45,6 +46,9 @@ func (r ResourceUpdatedHandler) GetConfig() (util.Config, string) {
} else if _, ok := r.Resource.(*v1.Secret); ok {
oldSHAData = util.GetSHAfromSecret(r.OldResource.(*v1.Secret).Data)
config = util.GetSecretConfig(r.Resource.(*v1.Secret))
} else if _, ok := r.Resource.(*csiv1.SecretProviderClassPodStatus); ok {
oldSHAData = util.GetSHAfromSecretProviderClassPodStatus(r.OldResource.(*csiv1.SecretProviderClassPodStatus).Status)
config = util.GetSecretProviderClassPodStatusConfig(r.Resource.(*csiv1.SecretProviderClassPodStatus))
} else {
logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource)
}
+9
View File
@@ -219,6 +219,7 @@ func PerformAction(clients kube.Clients, config util.Config, upgradeFuncs callba
typedAutoAnnotationEnabledValue, foundTypedAuto := annotations[config.TypedAutoAnnotation]
excludeConfigmapAnnotationValue, foundExcludeConfigmap := annotations[options.ConfigmapExcludeReloaderAnnotation]
excludeSecretAnnotationValue, foundExcludeSecret := annotations[options.SecretExcludeReloaderAnnotation]
excludeSecretProviderClassProviderAnnotationValue, foundExcludeSecretProviderClass := annotations[options.SecretProviderClassExcludeReloaderAnnotation]
if !found && !foundAuto && !foundTypedAuto && !foundSearchAnn {
annotations = upgradeFuncs.PodAnnotationsFunc(i)
@@ -239,6 +240,10 @@ func PerformAction(clients kube.Clients, config util.Config, upgradeFuncs callba
if foundExcludeSecret {
isResourceExcluded = checkIfResourceIsExcluded(config.ResourceName, excludeSecretAnnotationValue)
}
case constants.SecretProviderClassEnvVarPostfix:
if foundExcludeSecretProviderClass {
isResourceExcluded = checkIfResourceIsExcluded(config.ResourceName, excludeSecretProviderClassProviderAnnotationValue)
}
}
if isResourceExcluded {
@@ -355,6 +360,10 @@ func getVolumeMountName(volumes []v1.Volume, mountType string, volumeName string
}
}
}
} else if mountType == constants.SecretProviderClassEnvVarPostfix {
if volumes[i].CSI != nil && volumes[i].CSI.VolumeAttributes["secretProviderClass"] == volumeName {
return volumes[i].Name
}
}
}
+4
View File
@@ -30,6 +30,8 @@ var (
ConfigmapExcludeReloaderAnnotation = "configmaps.exclude.reloader.stakater.com/reload"
// SecretExcludeReloaderAnnotation is a comma separated list of secrets that excludes detecting changes on secrets
SecretExcludeReloaderAnnotation = "secrets.exclude.reloader.stakater.com/reload"
// SecretProviderClassExcludeReloaderAnnotation is a comma separated list of secret provider classes that excludes detecting changes on secret provider class
SecretProviderClassExcludeReloaderAnnotation = "secretproviderclass.exclude.reloader.stakater.com/reload"
// AutoSearchAnnotation is an annotation to detect changes in
// configmaps or triggers with the SearchMatchAnnotation
AutoSearchAnnotation = "reloader.stakater.com/search"
@@ -55,6 +57,8 @@ var (
EnableHA = false
// Url to send a request to instead of triggering a reload
WebhookUrl = ""
// EnableCsiIntegration Adds support to watch SecretProviderClassPodStatus and restart deployment based on it
EnableCSIIntegration = false
)
func ToArgoRolloutStrategy(s string) ArgoRolloutStrategy {
+12 -1
View File
@@ -4,9 +4,10 @@ import (
"github.com/stakater/Reloader/internal/pkg/constants"
"github.com/stakater/Reloader/internal/pkg/options"
v1 "k8s.io/api/core/v1"
csiv1 "sigs.k8s.io/secrets-store-csi-driver/apis/v1"
)
//Config contains rolling upgrade configuration parameters
// Config contains rolling upgrade configuration parameters
type Config struct {
Namespace string
ResourceName string
@@ -42,3 +43,13 @@ func GetSecretConfig(secret *v1.Secret) Config {
Type: constants.SecretEnvVarPostfix,
}
}
func GetSecretProviderClassPodStatusConfig(podStatus *csiv1.SecretProviderClassPodStatus) Config {
return Config{
Namespace: podStatus.Namespace,
ResourceName: podStatus.Status.SecretProviderClassName,
ResourceAnnotations: podStatus.Annotations,
SHAValue: GetSHAfromSecretProviderClassPodStatus(podStatus.Status),
Type: constants.SecretProviderClassEnvVarPostfix,
}
}
+11
View File
@@ -8,6 +8,7 @@ import (
"github.com/stakater/Reloader/internal/pkg/crypto"
v1 "k8s.io/api/core/v1"
csiv1 "sigs.k8s.io/secrets-store-csi-driver/apis/v1"
)
// ConvertToEnvVarName converts the given text into a usable env var
@@ -52,6 +53,16 @@ func GetSHAfromSecret(data map[string][]byte) string {
return crypto.GenerateSHA(strings.Join(values, ";"))
}
func GetSHAfromSecretProviderClassPodStatus(data csiv1.SecretProviderClassPodStatusStatus) string {
values := []string{}
for _, v := range data.Objects {
values = append(values, v.ID+"="+v.Version)
}
values = append(values, "SecretProviderClassName="+data.SecretProviderClassName)
sort.Strings(values)
return crypto.GenerateSHA(strings.Join(values, ";"))
}
type List []string
type Map map[string]string
+18
View File
@@ -11,6 +11,7 @@ import (
"github.com/sirupsen/logrus"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
csi "sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned"
)
// Clients struct exposes interfaces for kubernetes as well as openshift if available
@@ -18,6 +19,7 @@ type Clients struct {
KubernetesClient kubernetes.Interface
OpenshiftAppsClient appsclient.Interface
ArgoRolloutClient argorollout.Interface
CSIClient csi.Interface
}
var (
@@ -48,10 +50,18 @@ func GetClients() Clients {
logrus.Warnf("Unable to create ArgoRollout client error = %v", err)
}
var csiClient *csi.Clientset
csiClient, err = GetCSIClient()
if err != nil {
logrus.Warnf("Unable to create CSI client error = %v", err)
}
return Clients{
KubernetesClient: client,
OpenshiftAppsClient: appsClient,
ArgoRolloutClient: rolloutClient,
CSIClient: csiClient,
}
}
@@ -63,6 +73,14 @@ func GetArgoRolloutClient() (*argorollout.Clientset, error) {
return argorollout.NewForConfig(config)
}
func GetCSIClient() (*csi.Clientset, error) {
config, err := getConfig()
if err != nil {
return nil, err
}
return csi.NewForConfig(config)
}
func isOpenshift() bool {
client, err := GetKubernetesClient()
if err != nil {
+5 -3
View File
@@ -3,11 +3,13 @@ package kube
import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
csiv1 "sigs.k8s.io/secrets-store-csi-driver/apis/v1"
)
// ResourceMap are resources from where changes are going to be detected
var ResourceMap = map[string]runtime.Object{
"configMaps": &v1.ConfigMap{},
"secrets": &v1.Secret{},
"namespaces": &v1.Namespace{},
"configMaps": &v1.ConfigMap{},
"secrets": &v1.Secret{},
"namespaces": &v1.Namespace{},
"secretproviderclasspodstatuses": &csiv1.SecretProviderClassPodStatus{},
}