Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add labels and annotations field to SubNamespace resource #19

Merged
merged 28 commits into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
eccf069
Add labels and annotations field to SubNamespace resource
bells17 Sep 23, 2021
59b8588
Fix
bells17 Oct 5, 2021
71b1a32
Add comments
bells17 Oct 5, 2021
cff5a5a
Fix codes
bells17 Oct 12, 2021
05ae0c2
Merge branch 'main' into add-labels-and-annotations-field2
bells17 Oct 12, 2021
15fd9ee
Fix code
bells17 Oct 19, 2021
6b2cef1
Update chart version
bells17 Oct 19, 2021
8fac21a
Merge branch 'main' into add-labels-and-annotations-field2
bells17 Oct 19, 2021
edc2192
Add config params for keys of SubNamespace spec. labels/spec.annotati…
bells17 Nov 1, 2021
08dff57
Merge branch 'main' into add-labels-and-annotations-field2
bells17 Nov 1, 2021
c95b17e
Update chart version
bells17 Nov 1, 2021
bc02226
Fix configmap.yaml template
bells17 Nov 1, 2021
15d51ad
Update charts/accurate/MIGRATION.md
bells17 Nov 2, 2021
89517fb
Update charts/accurate/values.yaml
bells17 Nov 2, 2021
cc73a0b
Add --annotations and --labels option to kubectl-accurate sub create …
bells17 Nov 8, 2021
55c333e
Remove unused code
bells17 Nov 15, 2021
0e5f2ed
Merge branch 'main' into add-labels-and-annotations-field2
bells17 Nov 16, 2021
b737d26
Modify to propagate SubNamespace labels/annotations to descendant nam…
bells17 Nov 17, 2021
3a004b9
Fix
bells17 Nov 17, 2021
47876b5
Add testcases
bells17 Nov 17, 2021
4e5e4a0
Add docs for SubNamespace labels/annotations
bells17 Nov 17, 2021
f8f1134
Update docs
bells17 Nov 17, 2021
1302f9f
Remove SubNamespace DeleteFunc from NamespaceReconciler
bells17 Nov 18, 2021
d812f76
Update docs/config.md
bells17 Dec 1, 2021
5b89d8f
Add example values to descriptions
bells17 Dec 1, 2021
a2ba084
Add check that the namespace is a sub-namespace
bells17 Dec 1, 2021
5fa63b5
Add validations for spec.labels and spec.annotations fields
bells17 Dec 1, 2021
75d104c
Modify to skip when apierrors.IsNotFound(err) is true
bells17 Dec 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions api/v1/subnamespace_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,28 @@ const (
SubNamespaceConflict = SubNamespaceStatus("conflict")
)

// SubNamespaceSpec defines the desired state of SubNamespace
type SubNamespaceSpec struct {
ymmt2005 marked this conversation as resolved.
Show resolved Hide resolved
// Labels are the labels to be propagated to the sub-namespace
// +optional
Labels map[string]string `json:"labels,omitempty"`

// Annotations are the annotations to be propagated to the sub-namespace.
// +optional
Annotations map[string]string `json:"annotations,omitempty"`
}

//+kubebuilder:object:root=true

// SubNamespace is the Schema for the subnamespaces API
type SubNamespace struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec is the spec of SubNamespace.
// +optional
Spec SubNamespaceSpec `json:"spec,omitempty"`

// Status is the status of SubNamespace.
// +optional
Status SubNamespaceStatus `json:"status,omitempty"`
Expand Down
30 changes: 30 additions & 0 deletions api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions charts/accurate/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ controller:
# https://metallb.universe.tf/usage/#requesting-specific-ips
# - metallb.universe.tf/address-pool

# controller.config.subNamespaceLabelKeys -- Labels to be propagated to sub-namespaces from SubNamespace resource.
# It is also possible to specify a glob pattern that can be interpreted by Go's "path.Match" func.
# https://pkg.go.dev/path#Match
subNamespaceLabelKeys: []
# - app

# controller.config.subNamespaceAnnotationKeys -- Annotations to be propagated to sub-namespaces from SubNamespace resource.
# It is also possible to specify a glob pattern that can be interpreted by Go's "path.Match" func.
# https://pkg.go.dev/path#Match
subNamespaceAnnotationKeys: []
# - foo.bar/baz

# controller.config.watches -- List of GVK for namespace-scoped resources that can be propagated.
# Any namespace-scoped resource is allowed.
watches:
Expand Down
15 changes: 15 additions & 0 deletions charts/accurate/crds/accurate.cybozu.com_subnamespaces.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ spec:
type: string
metadata:
type: object
spec:
description: Spec is the spec of SubNamespace.
properties:
annotations:
additionalProperties:
type: string
description: Annotations are the annotations to be propagated to the
sub-namespace.
type: object
labels:
additionalProperties:
type: string
description: Labels are the labels to be propagated to the sub-namespace
type: object
type: object
status:
description: Status is the status of SubNamespace.
enum:
Expand Down
6 changes: 6 additions & 0 deletions charts/accurate/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ data:
{{- with .Values.controller.config.annotationKeys }}
annotationKeys: {{ toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.controller.config.subNamespaceLabelKeys }}
subNamespaceLabelKeys: {{ toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.controller.config.subNamespaceAnnotationKeys }}
subNamespaceAnnotationKeys: {{ toYaml . | nindent 6 }}
{{- end }}
watches: {{ toYaml .Values.controller.config.watches | nindent 6 }}
12 changes: 12 additions & 0 deletions charts/accurate/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ controller:
# https://metallb.universe.tf/usage/#requesting-specific-ips
# - metallb.universe.tf/address-pool

# controller.config.subNamespaceLabelKeys -- Labels to be propagated to sub-namespaces from SubNamespace resource.
# It is also possible to specify a glob pattern that can be interpreted by Go's "path.Match" func.
# https://pkg.go.dev/path#Match
subNamespaceLabelKeys: []
# - app

# controller.config.subNamespaceAnnotationKeys -- Annotations to be propagated to sub-namespaces from SubNamespace resource.
# It is also possible to specify a glob pattern that can be interpreted by Go's "path.Match" func.
# https://pkg.go.dev/path#Match
subNamespaceAnnotationKeys: []
# - foo.bar/baz

# controller.config.watches -- List of GVK for namespace-scoped resources that can be propagated.
# Any namespace-scoped resource is allowed.
watches:
Expand Down
10 changes: 6 additions & 4 deletions cmd/accurate-controller/sub/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,12 @@ func subMain(ns, addr string, port int) error {
return fmt.Errorf("failed to setup indexer for namespaces: %w", err)
}
if err := (&controllers.NamespaceReconciler{
Client: mgr.GetClient(),
LabelKeys: cfg.LabelKeys,
AnnotationKeys: cfg.AnnotationKeys,
Watched: watched,
Client: mgr.GetClient(),
LabelKeys: cfg.LabelKeys,
AnnotationKeys: cfg.AnnotationKeys,
SubNamespaceLabelKeys: cfg.SubNamespaceLabelKeys,
SubNamespaceAnnotationKeys: cfg.SubNamespaceAnnotationKeys,
Watched: watched,
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to create Namespace controller: %w", err)
}
Expand Down
14 changes: 10 additions & 4 deletions cmd/kubectl-accurate/sub/sub_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import (
)

type subCreateOpts struct {
streams genericclioptions.IOStreams
client client.Client
name string
parent string
streams genericclioptions.IOStreams
client client.Client
name string
parent string
labels map[string]string
annotations map[string]string
}

func newSubCreateCmd(streams genericclioptions.IOStreams, config *genericclioptions.ConfigFlags) *cobra.Command {
Expand All @@ -36,6 +38,8 @@ This effectively creates a namespace named NAME as a sub-namespace of NS.`,
},
}

cmd.Flags().StringToStringVar(&opts.labels, "labels", opts.labels, "the labels to be propagated to the sub-namespace. Example: a=b,c=d")
cmd.Flags().StringToStringVar(&opts.annotations, "annotations", opts.annotations, "the annotations to be propagated to the sub-namespace. Example: a=b,c=d")
return cmd
}

Expand Down Expand Up @@ -64,6 +68,8 @@ func (o *subCreateOpts) Run(ctx context.Context) error {
sn := &accuratev1.SubNamespace{}
sn.Namespace = o.parent
sn.Name = o.name
sn.Spec.Labels = o.labels
sn.Spec.Annotations = o.annotations

if err := o.client.Create(ctx, sn); err != nil {
return fmt.Errorf("failed to create a SubNamespace: %w", err)
Expand Down
7 changes: 7 additions & 0 deletions cmd/kubectl-accurate/sub/sub_move.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ func (o *subMoveOpts) Run(ctx context.Context) error {

fmt.Fprintf(o.streams.Out, "the parent has changed to %s\n", o.parent)

oldSN := &accuratev1.SubNamespace{}
if err := o.client.Get(ctx, client.ObjectKey{Name: o.name, Namespace: orig}, oldSN); err != nil {
return fmt.Errorf("failed to get original SubNamespace %s/%s: %w", orig, o.name, err)
}

if !o.orphan {
oldSN := &accuratev1.SubNamespace{}
oldSN.Namespace = orig
Expand All @@ -101,6 +106,8 @@ func (o *subMoveOpts) Run(ctx context.Context) error {
sn := &accuratev1.SubNamespace{}
sn.Namespace = o.parent
sn.Name = o.name
sn.Spec.Labels = oldSN.Spec.Labels
sn.Spec.Annotations = oldSN.Spec.Annotations
if err := o.client.Create(ctx, sn); err != nil {
return fmt.Errorf("failed to create SubNamespace in %s: %w", o.parent, err)
}
Expand Down
15 changes: 15 additions & 0 deletions config/crd/bases/accurate.cybozu.com_subnamespaces.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@ spec:
type: string
metadata:
type: object
spec:
description: Spec is the spec of SubNamespace.
properties:
annotations:
additionalProperties:
type: string
description: Annotations are the annotations to be propagated to the
sub-namespace.
type: object
labels:
additionalProperties:
type: string
description: Labels are the labels to be propagated to the sub-namespace
type: object
type: object
status:
description: Status is the status of SubNamespace.
enum:
Expand Down
89 changes: 71 additions & 18 deletions controllers/namespace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,31 @@ import (
"path"
"reflect"

accuratev1 "github.com/cybozu-go/accurate/api/v1"
"github.com/cybozu-go/accurate/pkg/constants"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)

// NamespaceReconciler reconciles a Namespace object
type NamespaceReconciler struct {
client.Client
LabelKeys []string
AnnotationKeys []string
Watched []*unstructured.Unstructured
LabelKeys []string
AnnotationKeys []string
SubNamespaceLabelKeys []string
SubNamespaceAnnotationKeys []string
Watched []*unstructured.Unstructured
}

var _ reconcile.Reconciler = &NamespaceReconciler{}
Expand Down Expand Up @@ -92,6 +100,29 @@ func (r *NamespaceReconciler) propagateMeta(ctx context.Context, ns, parent *cor
ns.Annotations[k] = v
}
}

if _, ok := ns.Labels[constants.LabelParent]; ok {
subNS := &accuratev1.SubNamespace{}
err := r.Get(ctx, types.NamespacedName{Name: ns.Name, Namespace: parent.Name}, subNS)
if err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to get sub namespace %s/%s: %w", ns.Name, parent.Name, err)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if apierrors.IsNotFound(err) is true?
In that case, subNS is invalid. Although the following code would work for such invalid data,
I want to check them and skip code using subNS.


for k, v := range subNS.Spec.Labels {
if ok := r.matchSubNamespaceLabelKey(k); ok {
ns.Labels[k] = v
}
}
for k, v := range subNS.Spec.Annotations {
if ok := r.matchSubNamespaceAnnotationKey(k); ok {
if ns.Annotations == nil {
ns.Annotations = make(map[string]string)
}
ns.Annotations[k] = v
}
}
}

if !reflect.DeepEqual(ns.ObjectMeta, orig.ObjectMeta) {
if err := r.Update(ctx, ns); err != nil {
return fmt.Errorf("failed to propagate labels/annotations for namespace %s: %w", ns.Name, err)
Expand All @@ -101,25 +132,19 @@ func (r *NamespaceReconciler) propagateMeta(ctx context.Context, ns, parent *cor
}

func (r *NamespaceReconciler) matchLabelKey(key string) bool {
for _, l := range r.LabelKeys {
// The glob pattern has been verified to be in the valid format when reading the config file.
if ok, _ := path.Match(l, key); ok {
return true
}
}

return false
return matchKey(key, r.LabelKeys)
}

func (r *NamespaceReconciler) matchAnnotationKey(key string) bool {
for _, a := range r.AnnotationKeys {
// The glob pattern has been verified to be in the valid format when reading the config file.
if ok, _ := path.Match(a, key); ok {
return true
}
}
return matchKey(key, r.AnnotationKeys)
}

return false
func (r *NamespaceReconciler) matchSubNamespaceLabelKey(key string) bool {
return matchKey(key, r.SubNamespaceLabelKeys)
}

func (r *NamespaceReconciler) matchSubNamespaceAnnotationKey(key string) bool {
return matchKey(key, r.SubNamespaceAnnotationKeys)
}

func (r *NamespaceReconciler) propagateResource(ctx context.Context, res *unstructured.Unstructured, parent, ns string) error {
Expand Down Expand Up @@ -351,7 +376,35 @@ func (r *NamespaceReconciler) reconcileTemplateNamespace(ctx context.Context, ns

// SetupWithManager sets up the controller with the Manager.
func (r *NamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
subNSHandler := func(o client.Object, q workqueue.RateLimitingInterface) {
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
Name: o.GetName(),
}})
}

return ctrl.NewControllerManagedBy(mgr).
For(&corev1.Namespace{}).
Watches(&source.Kind{Type: &accuratev1.SubNamespace{}}, handler.Funcs{
CreateFunc: func(ev event.CreateEvent, q workqueue.RateLimitingInterface) {
subNSHandler(ev.Object, q)
},
UpdateFunc: func(ev event.UpdateEvent, q workqueue.RateLimitingInterface) {
if ev.ObjectNew.GetDeletionTimestamp() != nil {
return
}
subNSHandler(ev.ObjectOld, q)
},
}).
Complete(r)
}

func matchKey(key string, list []string) bool {
for _, l := range list {
// The glob pattern has been verified to be in the valid format when reading the config file.
if ok, _ := path.Match(l, key); ok {
return true
}
}

return false
}
Loading