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

Propagate annotations #223

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion api/v1alpha1/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ const (
AnnotationNodeHost = "ydb.tech/node-host"
AnnotationNodeDomain = "ydb.tech/node-domain"

AnnotationValueTrue = "true"
RemoteFinalizerKey = "ydb.tech/remote-finalizer"
StorageFinalizerKey = "ydb.tech/storage-finalizer"

legacyTenantNameFormat = "/%s/%s"
)
Expand Down
87 changes: 79 additions & 8 deletions internal/annotations/annotations.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,86 @@
package annotations

import (
corev1 "k8s.io/api/core/v1"

"github.com/ydb-platform/ydb-kubernetes-operator/api/v1alpha1"
)

const (
PrimaryResourceStorageAnnotation = "ydb.tech/primary-resource-storage"
PrimaryResourceDatabaseAnnotation = "ydb.tech/primary-resource-database"
RemoteResourceVersionAnnotation = "ydb.tech/remote-resource-version"
ConfigurationChecksum = "ydb.tech/configuration-checksum"
StorageFinalizerKey = "ydb.tech/storage-finalizer"
RemoteFinalizerKey = "ydb.tech/remote-finalizer"
LastAppliedAnnotation = "ydb.tech/last-applied"
PrimaryResourceStorage = "ydb.tech/primary-resource-storage"
PrimaryResourceDatabase = "ydb.tech/primary-resource-database"
RemoteResourceVersion = "ydb.tech/remote-resource-version"
ConfigurationChecksum = "ydb.tech/configuration-checksum"
LastApplied = "ydb.tech/last-applied"
)

var (
AcceptedDNSPolicy = []string{
string(corev1.DNSClusterFirstWithHostNet),
string(corev1.DNSClusterFirst),
string(corev1.DNSDefault),
string(corev1.DNSNone),
}
SupportedAnnotations = map[string]struct{}{
v1alpha1.AnnotationSkipInitialization: {},
v1alpha1.AnnotationUpdateStrategyOnDelete: {},
v1alpha1.AnnotationUpdateDNSPolicy: {},
v1alpha1.AnnotationDisableLivenessProbe: {},
v1alpha1.AnnotationDataCenter: {},
v1alpha1.AnnotationGRPCPublicHost: {},
v1alpha1.AnnotationNodeHost: {},
v1alpha1.AnnotationNodeDomain: {},
}
)

type Annotations map[string]string

func Common(objAnnotations Annotations) Annotations {
annotations := Annotations{}

annotations.Merge(getSupportedAnnotations(objAnnotations))

return annotations
}

func (an Annotations) Merge(other map[string]string) map[string]string {
if other == nil {
return an
}

for k, v := range other {
an[k] = v
}

return an
}

func (an Annotations) AsMap() map[string]string {
return an
}

func (an Annotations) Copy() Annotations {
res := Annotations{}

for k, v := range an {
res[k] = v
}

return res
}

func getSupportedAnnotations(annotations map[string]string) map[string]string {
common := make(map[string]string)

for key, value := range annotations {
if _, exists := SupportedAnnotations[key]; exists {
common[key] = value
}
}

return common
}

func CompareLastAppliedAnnotation(map1, map2 map[string]string) bool {
value1 := getLastAppliedAnnotation(map1)
value2 := getLastAppliedAnnotation(map2)
Expand All @@ -18,7 +89,7 @@ func CompareLastAppliedAnnotation(map1, map2 map[string]string) bool {

func getLastAppliedAnnotation(annotations map[string]string) string {
for key, value := range annotations {
if key == LastAppliedAnnotation {
if key == LastApplied {
return value
}
}
Expand Down
47 changes: 47 additions & 0 deletions internal/annotations/annotations_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package annotations_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
ydbannotations "github.com/ydb-platform/ydb-kubernetes-operator/internal/annotations"
)

func TestLabels(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Label suite")
}

var _ = Describe("Testing annotations", func() {
It("merges two sets of annotations", func() {
fstLabels := ydbannotations.Annotations{
"a": "a",
"b": "b",
}

sndLabels := ydbannotations.Annotations{
"c": "c",
"d": "d",
}

Expect(fstLabels.Merge(sndLabels)).To(BeEquivalentTo(map[string]string{
"a": "a",
"b": "b",
"c": "c",
"d": "d",
}))
})

It("sets correct defaults", func() {
Expect(ydbannotations.Common(map[string]string{
"ydb.tech/skip-initialization": "true",
"ydb.tech/node-host": "ydb-testing.k8s-c.yandex.net",
"ydb.tech/last-applied": "some-body",
"sample-annotation": "test",
})).To(BeEquivalentTo(map[string]string{
"ydb.tech/skip-initialization": "true",
"ydb.tech/node-host": "ydb-testing.k8s-c.yandex.net",
}))
})
})
21 changes: 12 additions & 9 deletions internal/controllers/database/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package database
import (
"context"
"fmt"
"strconv"

ydb "github.com/ydb-platform/ydb-go-sdk/v3"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -34,15 +35,17 @@ func (r *Reconciler) setInitPipelineStatus(
}

// This block is special internal logic that skips all Database initialization.
if value, ok := database.Annotations[v1alpha1.AnnotationSkipInitialization]; ok && value == v1alpha1.AnnotationValueTrue {
r.Log.Info("Database initialization disabled (with annotation), proceed with caution")
r.Recorder.Event(
database,
corev1.EventTypeWarning,
"SkippingInit",
"Skipping initialization due to skip annotation present, be careful!",
)
return r.setInitDatabaseCompleted(ctx, database, "Database initialization not performed because initialization is skipped")
if value, ok := database.Annotations[v1alpha1.AnnotationSkipInitialization]; ok {
if isTrue, _ := strconv.ParseBool(value); isTrue {
r.Log.Info("Database initialization disabled (with annotation), proceed with caution")
r.Recorder.Event(
database,
corev1.EventTypeWarning,
"SkippingInit",
"Skipping initialization due to skip annotation present, be careful!",
)
return r.setInitDatabaseCompleted(ctx, database, "Database initialization not performed because initialization is skipped")
}
}

if meta.IsStatusConditionTrue(database.Status.Conditions, OldDatabaseInitializedCondition) {
Expand Down
34 changes: 17 additions & 17 deletions internal/controllers/remotedatabasenodeset/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
apilabels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -25,7 +25,7 @@ import (
"github.com/ydb-platform/ydb-kubernetes-operator/api/v1alpha1"
ydbannotations "github.com/ydb-platform/ydb-kubernetes-operator/internal/annotations"
. "github.com/ydb-platform/ydb-kubernetes-operator/internal/controllers/constants" //nolint:revive,stylecheck
ydblabels "github.com/ydb-platform/ydb-kubernetes-operator/internal/labels"
"github.com/ydb-platform/ydb-kubernetes-operator/internal/labels"
"github.com/ydb-platform/ydb-kubernetes-operator/internal/resources"
)

Expand Down Expand Up @@ -72,15 +72,15 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object. This is equivalent
// to registering our finalizer.
if !controllerutil.ContainsFinalizer(remoteDatabaseNodeSet, ydbannotations.RemoteFinalizerKey) {
controllerutil.AddFinalizer(remoteDatabaseNodeSet, ydbannotations.RemoteFinalizerKey)
if !controllerutil.ContainsFinalizer(remoteDatabaseNodeSet, v1alpha1.RemoteFinalizerKey) {
controllerutil.AddFinalizer(remoteDatabaseNodeSet, v1alpha1.RemoteFinalizerKey)
if err := r.RemoteClient.Update(ctx, remoteDatabaseNodeSet); err != nil {
return ctrl.Result{RequeueAfter: DefaultRequeueDelay}, err
}
}
} else {
// The object is being deleted
if controllerutil.ContainsFinalizer(remoteDatabaseNodeSet, ydbannotations.RemoteFinalizerKey) {
if controllerutil.ContainsFinalizer(remoteDatabaseNodeSet, v1alpha1.RemoteFinalizerKey) {
// our finalizer is present, so lets handle any external dependency
if err := r.deleteExternalResources(ctx, remoteDatabaseNodeSet); err != nil {
// if fail to delete the external dependency here, return with error
Expand All @@ -89,7 +89,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
}

// remove our finalizer from the list and update it.
controllerutil.RemoveFinalizer(remoteDatabaseNodeSet, ydbannotations.RemoteFinalizerKey)
controllerutil.RemoveFinalizer(remoteDatabaseNodeSet, v1alpha1.RemoteFinalizerKey)
if err := r.RemoteClient.Update(ctx, remoteDatabaseNodeSet); err != nil {
return ctrl.Result{RequeueAfter: DefaultRequeueDelay}, err
}
Expand Down Expand Up @@ -119,7 +119,7 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, remoteCluster *cluster.C
requests := make([]reconcile.Request, 0)

annotations := mapObj.GetAnnotations()
primaryResourceName, exist := annotations[ydbannotations.PrimaryResourceDatabaseAnnotation]
primaryResourceName, exist := annotations[ydbannotations.PrimaryResourceDatabase]
if exist {
databaseNodeSets := &v1alpha1.DatabaseNodeSetList{}
if err := r.Client.List(
Expand Down Expand Up @@ -192,32 +192,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, remoteCluster *cluster.C
Complete(r)
}

func buildLocalSelector() (labels.Selector, error) {
labelRequirements := []labels.Requirement{}
localClusterRequirement, err := labels.NewRequirement(
ydblabels.RemoteClusterKey,
func buildLocalSelector() (apilabels.Selector, error) {
labelRequirements := []apilabels.Requirement{}
localClusterRequirement, err := apilabels.NewRequirement(
labels.RemoteClusterKey,
selection.Exists,
[]string{},
)
if err != nil {
return nil, err
}
labelRequirements = append(labelRequirements, *localClusterRequirement)
return labels.NewSelector().Add(labelRequirements...), nil
return apilabels.NewSelector().Add(labelRequirements...), nil
}

func BuildRemoteSelector(remoteCluster string) (labels.Selector, error) {
labelRequirements := []labels.Requirement{}
remoteClusterRequirement, err := labels.NewRequirement(
ydblabels.RemoteClusterKey,
func BuildRemoteSelector(remoteCluster string) (apilabels.Selector, error) {
labelRequirements := []apilabels.Requirement{}
remoteClusterRequirement, err := apilabels.NewRequirement(
labels.RemoteClusterKey,
selection.Equals,
[]string{remoteCluster},
)
if err != nil {
return nil, err
}
labelRequirements = append(labelRequirements, *remoteClusterRequirement)
return labels.NewSelector().Add(labelRequirements...), nil
return apilabels.NewSelector().Add(labelRequirements...), nil
}

func (r *Reconciler) deleteExternalResources(
Expand Down
32 changes: 16 additions & 16 deletions internal/controllers/remotedatabasenodeset/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,25 +529,25 @@ var _ = Describe("RemoteDatabaseNodeSet controller tests", func() {
return err
}

primaryResourceStorage, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceStorageAnnotation]
primaryResourceStorage, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceStorage]
if !exist {
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.PrimaryResourceStorageAnnotation, remoteSecret.Name)
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.PrimaryResourceStorage, remoteSecret.Name)
}
if primaryResourceStorage != foundRemoteStorageNodeSet.Spec.StorageRef.Name {
return fmt.Errorf("primaryResourceName %s does not equal storageRef name %s", primaryResourceStorage, foundRemoteDatabaseNodeSet.Spec.DatabaseRef.Name)
}

primaryResourceDatabase, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceDatabaseAnnotation]
primaryResourceDatabase, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceDatabase]
if !exist {
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.PrimaryResourceDatabaseAnnotation, remoteSecret.Name)
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.PrimaryResourceDatabase, remoteSecret.Name)
}
if primaryResourceDatabase != foundRemoteDatabaseNodeSet.Spec.DatabaseRef.Name {
return fmt.Errorf("primaryResourceName %s does not equal databaseRef name %s", primaryResourceDatabase, foundRemoteDatabaseNodeSet.Spec.DatabaseRef.Name)
}

remoteRV, exist := remoteSecret.Annotations[ydbannotations.RemoteResourceVersionAnnotation]
remoteRV, exist := remoteSecret.Annotations[ydbannotations.RemoteResourceVersion]
if !exist {
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.RemoteResourceVersionAnnotation, remoteSecret.Name)
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.RemoteResourceVersion, remoteSecret.Name)
}
if localSecret.GetResourceVersion() != remoteRV {
return fmt.Errorf("localRV %s does not equal remoteRV %s", localSecret.GetResourceVersion(), remoteRV)
Expand Down Expand Up @@ -620,22 +620,22 @@ var _ = Describe("RemoteDatabaseNodeSet controller tests", func() {
return err
}

_, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceStorageAnnotation]
_, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceStorage]
if exist {
return fmt.Errorf("annotation %s still exist on remoteSecret %s", ydbannotations.PrimaryResourceStorageAnnotation, remoteSecret.Name)
return fmt.Errorf("annotation %s still exist on remoteSecret %s", ydbannotations.PrimaryResourceStorage, remoteSecret.Name)
}

primaryResourceDatabase, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceDatabaseAnnotation]
primaryResourceDatabase, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceDatabase]
if !exist {
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.PrimaryResourceDatabaseAnnotation, remoteSecret.Name)
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.PrimaryResourceDatabase, remoteSecret.Name)
}
if primaryResourceDatabase != foundRemoteDatabaseNodeSet.Spec.DatabaseRef.Name {
return fmt.Errorf("primaryResourceName %s does not equal databaseRef name %s", primaryResourceDatabase, foundRemoteDatabaseNodeSet.Spec.DatabaseRef.Name)
}

remoteRV, exist := remoteSecret.Annotations[ydbannotations.RemoteResourceVersionAnnotation]
remoteRV, exist := remoteSecret.Annotations[ydbannotations.RemoteResourceVersion]
if !exist {
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.RemoteResourceVersionAnnotation, remoteSecret.Name)
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.RemoteResourceVersion, remoteSecret.Name)
}
if localSecret.GetResourceVersion() != remoteRV {
return fmt.Errorf("localRV %s does not equal remoteRV %s", localSecret.GetResourceVersion(), remoteRV)
Expand Down Expand Up @@ -686,17 +686,17 @@ var _ = Describe("RemoteDatabaseNodeSet controller tests", func() {
return err
}

primaryResourceDatabase, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceDatabaseAnnotation]
primaryResourceDatabase, exist := remoteSecret.Annotations[ydbannotations.PrimaryResourceDatabase]
if !exist {
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.PrimaryResourceDatabaseAnnotation, remoteSecret.Name)
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.PrimaryResourceDatabase, remoteSecret.Name)
}
if primaryResourceDatabase != foundRemoteDatabaseNodeSet.Spec.DatabaseRef.Name {
return fmt.Errorf("primaryResourceName %s does not equal databaseRef name %s", primaryResourceDatabase, foundRemoteDatabaseNodeSet.Spec.DatabaseRef.Name)
}

remoteRV, exist := remoteSecret.Annotations[ydbannotations.RemoteResourceVersionAnnotation]
remoteRV, exist := remoteSecret.Annotations[ydbannotations.RemoteResourceVersion]
if !exist {
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.RemoteResourceVersionAnnotation, remoteSecret.Name)
return fmt.Errorf("annotation %s does not exist on remoteSecret %s", ydbannotations.RemoteResourceVersion, remoteSecret.Name)
}
if localSecret.GetResourceVersion() != remoteRV {
return fmt.Errorf("localRV %s does not equal remoteRV %s", localSecret.GetResourceVersion(), remoteRV)
Expand Down
4 changes: 2 additions & 2 deletions internal/controllers/remotedatabasenodeset/remote_objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func (r *Reconciler) removeUnusedRemoteObjects(
// Remove annotation if no one another DatabaseNodeSet
if !existInDatabase {
// Try to update existing object in local cluster by rawPatch
patch := []byte(fmt.Sprintf(`{"metadata": {"annotations": {"%s": null}}}`, ydbannotations.PrimaryResourceStorageAnnotation))
patch := []byte(fmt.Sprintf(`{"metadata": {"annotations": {"%s": null}}}`, ydbannotations.PrimaryResourceStorage))
updateErr := r.Client.Patch(ctx, localObj, client.RawPatch(types.StrategicMergePatchType, patch))
if updateErr != nil {
r.Recorder.Event(
Expand All @@ -298,7 +298,7 @@ func (r *Reconciler) removeUnusedRemoteObjects(
}

// Delete resource if annotation `ydb.tech/primary-resource-storage` does not exist
_, existInStorage := localObj.GetAnnotations()[ydbannotations.PrimaryResourceStorageAnnotation]
_, existInStorage := localObj.GetAnnotations()[ydbannotations.PrimaryResourceStorage]
if !existInStorage {
// Try to delete unused resource from local cluster
deleteErr := r.Client.Delete(ctx, localObj)
Expand Down
Loading
Loading