...

Source file src/edge-infra.dev/pkg/edge/externalsecrets/external_secrets.go

Documentation: edge-infra.dev/pkg/edge/externalsecrets

     1  package externalsecrets
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"time"
     7  
     8  	goext "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
     9  	corev1 "k8s.io/api/core/v1"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
    12  
    13  	"edge-infra.dev/pkg/edge/constants"
    14  	workloads "edge-infra.dev/pkg/edge/constants/api/workload"
    15  )
    16  
    17  const (
    18  	SecretName      = "gcp-creds" //nolint: gosec
    19  	SecretNamespace = "external-secrets"
    20  	SecretKey       = "key.json"
    21  
    22  	gcpProvider          = "gcp-provider"
    23  	clusterSecretStore   = "ClusterSecretStore"
    24  	DockerPullSecretType = "docker-registry"
    25  )
    26  
    27  var (
    28  	defaultInterval      = time.Minute
    29  	ErrNoName            = errors.New("external secret name must be set")
    30  	ErrNoNamespace       = errors.New("external secret namespace or namespace selector must be set")
    31  	ErrNameSpaceConflict = errors.New("cannot set both namesapce and namesapce selector for an external secret")
    32  	ErrNoPath            = errors.New("external secret path must be set")
    33  	ErrNoProjectID       = errors.New("external secret projectID must be set")
    34  )
    35  
    36  // ExternalSecret
    37  type ExternalSecret struct {
    38  	name            string
    39  	namespace       string
    40  	secretStoreKind string
    41  	secretStoreName string
    42  	path            string
    43  	k8sSecretName   string
    44  	projectID       string
    45  	template        *goext.ExternalSecretTemplate
    46  	dataFieldMap    map[string]map[string]string
    47  	dataMap         map[string]string
    48  	refreshInterval time.Duration
    49  	creationPolicy  goext.ExternalSecretCreationPolicy
    50  	labels          map[string]string
    51  }
    52  
    53  func DefaultExternalSecret() *ExternalSecret {
    54  	return &ExternalSecret{
    55  		dataMap:         map[string]string{},
    56  		dataFieldMap:    map[string]map[string]string{},
    57  		secretStoreKind: clusterSecretStore,
    58  		secretStoreName: gcpProvider,
    59  		refreshInterval: defaultInterval,
    60  		creationPolicy:  goext.CreatePolicyOwner,
    61  	}
    62  }
    63  
    64  // DockerConfig the secretKey would be the secret manager's key without a .
    65  // Where field is dockerconfigjson
    66  // secretRef is dockerconfigjson
    67  func (p *ExternalSecret) DockerConfig(secretName, field string) *ExternalSecret {
    68  	templateString := fmt.Sprintf("{{ .%s }}", field)
    69  	p.template = &goext.ExternalSecretTemplate{
    70  		EngineVersion: goext.TemplateEngineV2,
    71  		Type:          corev1.SecretTypeDockerConfigJson,
    72  		Data:          map[string]string{".dockerconfigjson": templateString},
    73  	}
    74  
    75  	return p.MapSecretFieldToK8sSecretKey(secretName, field, field)
    76  }
    77  
    78  func (p *ExternalSecret) SetSecretType(secretType corev1.SecretType) *ExternalSecret {
    79  	p.template = &goext.ExternalSecretTemplate{
    80  		Type: secretType,
    81  	}
    82  	return p
    83  }
    84  
    85  func (p *ExternalSecret) Namespace(ns string) *ExternalSecret {
    86  	p.namespace = ns
    87  	return p
    88  }
    89  
    90  func (p *ExternalSecret) Name(name string) *ExternalSecret {
    91  	p.name = name
    92  	return p
    93  }
    94  
    95  func (p *ExternalSecret) Path(path string) *ExternalSecret {
    96  	p.path = path
    97  	return p
    98  }
    99  
   100  func (p *ExternalSecret) K8sSecretName(k8sSecretName string) *ExternalSecret {
   101  	p.k8sSecretName = k8sSecretName
   102  	return p
   103  }
   104  
   105  func (p *ExternalSecret) ProjectID(projectID string) *ExternalSecret {
   106  	p.projectID = projectID
   107  	return p
   108  }
   109  
   110  func (p *ExternalSecret) Labels(labels map[string]string) *ExternalSecret {
   111  	if labels == nil {
   112  		p.labels = map[string]string{}
   113  		return p
   114  	}
   115  	p.labels = labels
   116  	return p
   117  }
   118  
   119  func (p *ExternalSecret) MapSecretFieldToK8sSecretKey(secretManagerSecretName, secretManagerField, k8sSecretKey string) *ExternalSecret {
   120  	if secretManagerSecret, ok := p.dataFieldMap[secretManagerSecretName]; ok {
   121  		secretManagerSecret[secretManagerField] = k8sSecretKey
   122  	} else {
   123  		p.dataFieldMap[secretManagerSecretName] = map[string]string{secretManagerField: k8sSecretKey}
   124  	}
   125  	return p
   126  }
   127  
   128  // MapSecretToK8sSecretKey is used to map the entire secret manager secret to the this field in the k8s secret
   129  func (p *ExternalSecret) MapSecretToK8sSecretKey(secretManagerSecretName, k8sSecretKey string) *ExternalSecret {
   130  	p.dataMap[secretManagerSecretName] = k8sSecretKey
   131  	return p
   132  }
   133  
   134  func (p *ExternalSecret) Validate() error {
   135  	if p.name == "" {
   136  		return ErrNoName
   137  	}
   138  	if p.k8sSecretName == "" {
   139  		p.k8sSecretName = p.name
   140  	}
   141  	if p.projectID == "" {
   142  		return ErrNoProjectID
   143  	}
   144  	return nil
   145  }
   146  
   147  func (p *ExternalSecret) BuildClusterExternalSecret() (*goext.ClusterExternalSecret, error) {
   148  	err := p.Validate()
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	matchLabels := []string{"tenant"}
   153  	es := &goext.ClusterExternalSecret{
   154  		TypeMeta: metav1.TypeMeta{
   155  			APIVersion: goext.ExtSecretGroupVersionKind.GroupVersion().String(),
   156  			Kind:       goext.ClusterExtSecretGroupVersionKind.Kind,
   157  		},
   158  		ObjectMeta: metav1.ObjectMeta{
   159  			Name: p.name,
   160  			Labels: mergeStringMaps(p.labels, map[string]string{
   161  				constants.Tenant: p.projectID,
   162  			}),
   163  			Annotations: map[string]string{
   164  				// e,g,: manifests/namespaces/acme.yaml
   165  				kioutil.PathAnnotation: p.path,
   166  			},
   167  		},
   168  		Spec: goext.ClusterExternalSecretSpec{
   169  			ExternalSecretName: p.k8sSecretName,
   170  			ExternalSecretSpec: goext.ExternalSecretSpec{
   171  				SecretStoreRef: goext.SecretStoreRef{
   172  					Name: p.secretStoreName,
   173  					Kind: p.secretStoreKind,
   174  				},
   175  				Target: goext.ExternalSecretTarget{
   176  					Name:           p.k8sSecretName,
   177  					CreationPolicy: p.creationPolicy,
   178  				},
   179  				RefreshInterval: &metav1.Duration{
   180  					Duration: p.refreshInterval,
   181  				},
   182  			},
   183  			NamespaceSelectors: []*metav1.LabelSelector{
   184  				{
   185  					MatchExpressions: []metav1.LabelSelectorRequirement{
   186  						{Key: workloads.Label, Operator: metav1.LabelSelectorOpIn, Values: matchLabels},
   187  					},
   188  				},
   189  			},
   190  		},
   191  	}
   192  	var externalSecretData []goext.ExternalSecretData
   193  	for secretManagerSecretName, k8sSecretKey := range p.dataMap {
   194  		externalSecretData = append(externalSecretData, goext.ExternalSecretData{RemoteRef: goext.ExternalSecretDataRemoteRef{Key: secretManagerSecretName}, SecretKey: k8sSecretKey})
   195  	}
   196  	for secretManagerSecretName, secretManagerFieldToKeyMap := range p.dataFieldMap {
   197  		for secretManagerSecretField, k8sSecretKey := range secretManagerFieldToKeyMap {
   198  			externalSecretData = append(externalSecretData, goext.ExternalSecretData{RemoteRef: goext.ExternalSecretDataRemoteRef{Key: secretManagerSecretName, Property: secretManagerSecretField}, SecretKey: k8sSecretKey})
   199  		}
   200  	}
   201  	es.Spec.ExternalSecretSpec.Data = externalSecretData
   202  	es.Spec.ExternalSecretSpec.Target.Template = p.template
   203  	if p.namespace != "" {
   204  		es.ObjectMeta.Namespace = p.namespace
   205  	}
   206  
   207  	return es, nil
   208  }
   209  
   210  func (p *ExternalSecret) Build() (*goext.ExternalSecret, error) {
   211  	err := p.Validate()
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	es := &goext.ExternalSecret{
   216  		TypeMeta: metav1.TypeMeta{
   217  			APIVersion: goext.ExtSecretGroupVersionKind.GroupVersion().String(),
   218  			Kind:       goext.ExtSecretGroupVersionKind.Kind,
   219  		},
   220  		ObjectMeta: metav1.ObjectMeta{
   221  			Name: p.name,
   222  			Labels: mergeStringMaps(p.labels, map[string]string{
   223  				constants.Tenant: p.projectID,
   224  			}),
   225  			Annotations: map[string]string{
   226  				// e,g,: manifests/namespaces/acme.yaml
   227  				kioutil.PathAnnotation: p.path,
   228  			},
   229  		},
   230  		Spec: goext.ExternalSecretSpec{
   231  			SecretStoreRef: goext.SecretStoreRef{
   232  				Name: p.secretStoreName,
   233  				Kind: p.secretStoreKind,
   234  			},
   235  			Target: goext.ExternalSecretTarget{
   236  				Name:           p.k8sSecretName,
   237  				CreationPolicy: p.creationPolicy,
   238  			},
   239  			RefreshInterval: &metav1.Duration{
   240  				Duration: p.refreshInterval,
   241  			},
   242  		},
   243  	}
   244  	var externalSecretData []goext.ExternalSecretData
   245  	for secretManagerSecretName, k8sSecretKey := range p.dataMap {
   246  		externalSecretData = append(externalSecretData, goext.ExternalSecretData{RemoteRef: goext.ExternalSecretDataRemoteRef{Key: secretManagerSecretName}, SecretKey: k8sSecretKey})
   247  	}
   248  	for secretManagerSecretName, secretManagerFieldToKeyMap := range p.dataFieldMap {
   249  		for secretManagerSecretField, k8sSecretKey := range secretManagerFieldToKeyMap {
   250  			externalSecretData = append(externalSecretData, goext.ExternalSecretData{RemoteRef: goext.ExternalSecretDataRemoteRef{Key: secretManagerSecretName, Property: secretManagerSecretField}, SecretKey: k8sSecretKey})
   251  		}
   252  	}
   253  	es.Spec.Data = externalSecretData
   254  	es.Spec.Target.Template = p.template
   255  	if p.namespace != "" {
   256  		es.ObjectMeta.Namespace = p.namespace
   257  	}
   258  	return es, nil
   259  }
   260  
   261  // Merges mapOne into mapTwo and returns mapTwo
   262  func mergeStringMaps(mapOne, mapTwo map[string]string) map[string]string {
   263  	for k, v := range mapOne {
   264  		mapTwo[k] = v
   265  	}
   266  	return mapTwo
   267  }
   268  

View as plain text