Skip to content
This repository has been archived by the owner on Apr 18, 2024. It is now read-only.

Commit

Permalink
Merge pull request #26 from flanksource/copy-to-namespace-label-selector
Browse files Browse the repository at this point in the history
Copy to namespaces by namespace selector
  • Loading branch information
moshloop authored Feb 2, 2021
2 parents 029fa99 + bea715f commit d4f2312
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 2 deletions.
3 changes: 2 additions & 1 deletion api/v1/template_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ type JsonPatch struct {
}

type CopyToNamespaces struct {
Namespaces []string `json:"namespaces,omitempty"`
Namespaces []string `json:"namespaces,omitempty"`
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"`
}

// +kubebuilder:object:root=true
Expand Down
47 changes: 47 additions & 0 deletions config/crd/bases/templating.flanksource.com_templates.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,53 @@ spec:
copyToNamespaces:
description: Copy this object to other namespaces
properties:
namespaceSelector:
description: A label selector is a label query over a set of resources.
The result of matchLabels and matchExpressions are ANDed. An empty
label selector matches all objects. A null label selector matches
no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that
contains values, a key, and an operator that relates the
key and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
type: string
values:
description: values is an array of string values. If the
operator is In or NotIn, the values array must be non-empty.
If the operator is Exists or DoesNotExist, the values
array must be empty. This array is replaced during a
strategic merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator
is "In", and the values array contains only "value". The requirements
are ANDed.
type: object
type: object
namespaces:
items:
type: string
Expand Down
30 changes: 30 additions & 0 deletions config/deploy/crd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,36 @@ spec:
copyToNamespaces:
description: Copy this object to other namespaces
properties:
namespaceSelector:
description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
properties:
key:
description: key is the label key that the selector applies to.
type: string
operator:
description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
type: object
type: object
namespaces:
items:
type: string
Expand Down
30 changes: 30 additions & 0 deletions config/deploy/operator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,36 @@ spec:
copyToNamespaces:
description: Copy this object to other namespaces
properties:
namespaceSelector:
description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
properties:
key:
description: key is the label key that the selector applies to.
type: string
operator:
description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
type: object
type: object
namespaces:
items:
type: string
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require (
github.com/zalando/postgres-operator v1.6.0
go.uber.org/zap v1.15.0
gopkg.in/flanksource/yaml.v3 v3.1.1
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.19.4
k8s.io/apiextensions-apiserver v0.19.4
k8s.io/apimachinery v0.19.4
Expand Down
43 changes: 42 additions & 1 deletion k8s/template_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"regexp"
"sort"
"strings"
"text/template"

Expand Down Expand Up @@ -205,7 +206,12 @@ func (tm *TemplateManager) Run(ctx context.Context, template *templatev1.Templat
}

if template.Spec.CopyToNamespaces != nil {
for _, namespace := range template.Spec.CopyToNamespaces.Namespaces {
namespaces, err := tm.getNamespaces(ctx, *template.Spec.CopyToNamespaces)
if err != nil {
return errors.Wrap(err, "failed to get namespaces")
}

for _, namespace := range namespaces {
newResource := source.DeepCopy()
newResource.SetNamespace(namespace)
stripAnnotations(newResource)
Expand Down Expand Up @@ -357,6 +363,41 @@ func (tm *TemplateManager) getForEach(rawItem []byte, target map[string]interfac
return tm.JSONPath(target, fer.ForEach)
}

func (tm *TemplateManager) getNamespaces(ctx context.Context, copyToNamespaces templatev1.CopyToNamespaces) ([]string, error) {
namespaceMap := map[string]bool{}
namespaces := []string{}

for _, ns := range copyToNamespaces.Namespaces {
namespaceMap[ns] = true
}

if copyToNamespaces.NamespaceSelector != nil {
labelSelector, err := labelSelectorToString(*copyToNamespaces.NamespaceSelector)
if err != nil {
return nil, errors.Wrap(err, "failed to create label selector string")
}
options := metav1.ListOptions{
LabelSelector: labelSelector,
}
namespaceList, err := tm.CoreV1().Namespaces().List(ctx, options)
if err != nil {
return nil, errors.Wrap(err, "failed to list namespaces with label selector")
}

for _, ns := range namespaceList.Items {
namespaceMap[ns.Name] = true
}
}

for ns := range namespaceMap {
namespaces = append(namespaces, ns)
}

sort.Strings(namespaces)

return namespaces, nil
}

func (tm *TemplateManager) JSONPath(object interface{}, jsonpath string) (*ForEach, error) {
jsonpath = strings.TrimPrefix(jsonpath, "{{")
jsonpath = strings.TrimSuffix(jsonpath, "}}")
Expand Down

0 comments on commit d4f2312

Please sign in to comment.