Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ OS ?= $(shell uname -s)
OS := $(shell echo $(OS) | tr '[:upper:]' '[:lower:]')
K8S_LATEST_VER ?= $(shell curl -s https://dl.k8s.io/release/stable.txt)
export CONTROLLER_IMG ?= $(REGISTRY)/$(IMAGE_NAME)
TAG ?= v1.14.0
TAG ?= main

.PHONY: all
all: build
Expand Down
5 changes: 2 additions & 3 deletions api/v1beta1/zz_generated.deepcopy.go

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

2 changes: 1 addition & 1 deletion config/default/manager_auth_proxy_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ spec:
- --shard-key=
- --capi-onboard-annotation=
- "--v=5"
- "--version=v1.14.0"
- "--version=main"
- "--agent-in-mgmt-cluster=false"
env:
- name: GOMEMLIMIT
Expand Down
4 changes: 2 additions & 2 deletions config/default/manager_image_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ spec:
template:
spec:
initContainers:
- image: docker.io/projectsveltos/addon-controller:v1.14.0
- image: docker.io/projectsveltos/addon-controller:main
name: initialization
containers:
- image: docker.io/projectsveltos/addon-controller:v1.14.0
- image: docker.io/projectsveltos/addon-controller:main
name: controller
17 changes: 14 additions & 3 deletions controllers/drift_detection_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ func upgradeDriftDetectionDeploymentsInMgmtCluster(ctx context.Context, logger l
}

var patches []libsveltosv1beta1.Patch
var watchNamespaces []string
clusterNamespace, clusterName, clusterType, found :=
getClusterDataFromDriftDetectionManagerDeployment(&driftDetectionDeployments.Items[i])
if found {
Expand All @@ -207,10 +208,19 @@ func upgradeDriftDetectionDeploymentsInMgmtCluster(ctx context.Context, logger l
} else if patches == nil {
patches = globalPatches
}

watchNamespaces, err = getAgentWatchNamespaces(ctx, mgmtClient,
clusterNamespace, clusterName, clusterType, logger)
if err != nil {
logger.V(logs.LogInfo).Info(
fmt.Sprintf("cluster %s %s/%s failed to get drift detection watch namespaces: %v",
clusterType, clusterNamespace, clusterName, err))
allProcessed = false
}
}

err = upgradeDriftDetectionDeployment(ctx, config, mgmtClient, &driftDetectionDeployments.Items[i],
patches, logger)
patches, watchNamespaces, logger)
if err != nil {
logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to upgrade driftDetection deployment: %v", err))
allProcessed = false
Expand All @@ -226,14 +236,15 @@ func upgradeDriftDetectionDeploymentsInMgmtCluster(ctx context.Context, logger l
}

func upgradeDriftDetectionDeployment(ctx context.Context, config *rest.Config, c client.Client,
depl *appsv1.Deployment, patches []libsveltosv1beta1.Patch, logger logr.Logger) error {
depl *appsv1.Deployment, patches []libsveltosv1beta1.Patch, watchNamespaces []string,
logger logr.Logger) error {

exist, clusterNs, clusterName, clusterType := deplAssociatedClusterExist(ctx, c, depl, logger)
if exist {
logger.V(logs.LogDebug).Info(fmt.Sprintf("Upgrade drift detection deployment for cluster %s %s/%s",
clusterType, clusterNs, clusterName))
return deployDriftDetectionManagerInManagementCluster(ctx, config, clusterNs, clusterName,
"do-not-send-updates", clusterType, patches, logger)
"do-not-send-updates", clusterType, patches, watchNamespaces, logger)
}

return nil
Expand Down
5 changes: 5 additions & 0 deletions controllers/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ var (
RemoveDriftDetectionManagerFromManagementCluster = removeDriftDetectionManagerFromManagementCluster
GetDriftDetectionNamespaceInMgmtCluster = getDriftDetectionNamespaceInMgmtCluster
GetGlobalDriftDetectionManagerPatches = getGlobalDriftDetectionManagerPatches
GetAgentWatchNamespaces = getAgentWatchNamespaces
)

const (
AgentWatchNamespacesAnnotation = agentWatchNamespacesAnnotation
)

var (
Expand Down
81 changes: 81 additions & 0 deletions controllers/handlers_resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,4 +515,85 @@ var _ = Describe("Hash methods", func() {
Expect(reflect.DeepEqual(hash, expectedHash)).To(BeTrue())
}
})

It("ResourcesHash changes when the target Cluster's watch-namespaces annotation changes, "+
"only in ContinuousWithDriftDetection mode", func() {
namespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: randomString(),
},
}
Expect(testEnv.Create(context.TODO(), namespace)).To(Succeed())
Expect(waitForObject(context.TODO(), testEnv, namespace)).To(Succeed())

cluster := &libsveltosv1beta1.SveltosCluster{
ObjectMeta: metav1.ObjectMeta{
Name: randomString(),
Namespace: namespace.Name,
},
}
Expect(testEnv.Create(context.TODO(), cluster)).To(Succeed())
Expect(waitForObject(context.TODO(), testEnv, cluster)).To(Succeed())

newClusterSummary := func(syncMode configv1beta1.SyncMode) *configv1beta1.ClusterSummary {
cs := &configv1beta1.ClusterSummary{
ObjectMeta: metav1.ObjectMeta{
Name: randomString(),
Namespace: namespace.Name,
},
Spec: configv1beta1.ClusterSummarySpec{
ClusterNamespace: cluster.Namespace,
ClusterName: cluster.Name,
ClusterType: libsveltosv1beta1.ClusterTypeSveltos,
ClusterProfileSpec: configv1beta1.Spec{
SyncMode: syncMode,
},
},
}
Expect(testEnv.Create(context.TODO(), cs)).To(Succeed())
Expect(waitForObject(context.TODO(), testEnv, cs)).To(Succeed())
return cs
}

driftClusterSummary := newClusterSummary(configv1beta1.SyncModeContinuousWithDriftDetection)
beforeHash, err := controllers.ResourcesHash(context.TODO(), testEnv, driftClusterSummary,
textlogger.NewLogger(textlogger.NewConfig()))
Expect(err).To(BeNil())

continuousClusterSummary := newClusterSummary(configv1beta1.SyncModeContinuous)
continuousBeforeHash, err := controllers.ResourcesHash(context.TODO(), testEnv, continuousClusterSummary,
textlogger.NewLogger(textlogger.NewConfig()))
Expect(err).To(BeNil())

updatedWatchNamespaces := "ns-a,ns-b"
cluster.Annotations = map[string]string{
controllers.AgentWatchNamespacesAnnotation: updatedWatchNamespaces,
}
Expect(testEnv.Update(context.TODO(), cluster)).To(Succeed())

// waitForObject only confirms the object still exists, which for an Update (unlike a
// Create) is trivially already true: it says nothing about whether this specific
// annotation value has propagated through testEnv's cache yet. getClusterProfileSpecHash
// reads through that same cache (getManagementClusterClient), so poll for the real value
// there instead, or afterHash below could still observe the pre-update annotations.
Eventually(func() bool {
current := &libsveltosv1beta1.SveltosCluster{}
if err := testEnv.Get(context.TODO(), client.ObjectKeyFromObject(cluster), current); err != nil {
return false
}
return current.Annotations[controllers.AgentWatchNamespacesAnnotation] == updatedWatchNamespaces
}, timeout, pollingInterval).Should(BeTrue())

afterHash, err := controllers.ResourcesHash(context.TODO(), testEnv, driftClusterSummary,
textlogger.NewLogger(textlogger.NewConfig()))
Expect(err).To(BeNil())
Expect(reflect.DeepEqual(beforeHash, afterHash)).To(BeFalse())

// Same annotation change must NOT affect the hash outside ContinuousWithDriftDetection:
// nothing reads --watch-namespaces in that mode, so it would just be wasted cost.
continuousAfterHash, err := controllers.ResourcesHash(context.TODO(), testEnv, continuousClusterSummary,
textlogger.NewLogger(textlogger.NewConfig()))
Expect(err).To(BeNil())
Expect(reflect.DeepEqual(continuousBeforeHash, continuousAfterHash)).To(BeTrue())
})
})
61 changes: 58 additions & 3 deletions controllers/handlers_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,21 @@ func processDeployedGVKs(ctx context.Context, isMgmtCluster bool, remoteConfig *
LabelSelector: labels.Set(labelSelector.MatchLabels).String(),
}

// Only the remote (managed) cluster is ever reached with a credential restricted by
// agentWatchNamespacesAnnotation; local (management-cluster) deploys use addon-controller's
// own full-privilege client and are unaffected. Unlike drift-detection-manager, this is
// honored unconditionally: no license check, agentless or not (see the annotation's doc
// comment). Without it, a genuinely RBAC-restricted credential can't deploy at all.
var watchNamespaces []string
if !isMgmtCluster {
watchNamespaces, err = getAgentWatchNamespaces(ctx, getManagementClusterClient(),
clusterSummary.Spec.ClusterNamespace, clusterSummary.Spec.ClusterName,
clusterSummary.Spec.ClusterType, logger)
if err != nil {
return nil, err
}
}

for i := range deployedGVKs {
// TODO: move this to separate method
logger.V(logs.LogDebug).Info(fmt.Sprintf("removing stale resources for GVK %s", deployedGVKs[i].String()))
Expand All @@ -1125,7 +1140,8 @@ func processDeployedGVKs(ctx context.Context, isMgmtCluster bool, remoteConfig *
Resource: mapping.Resource.Resource,
}

list, err := d.Resource(resourceId).List(ctx, listOptions)
namespaced := mapping.Scope.Name() == meta.RESTScopeNameNamespace
items, err := listDeployedResources(ctx, d, resourceId, &listOptions, namespaced, watchNamespaces)
if err != nil {
return nil, err
}
Expand All @@ -1139,8 +1155,8 @@ func processDeployedGVKs(ctx context.Context, isMgmtCluster bool, remoteConfig *
expectedDeploymentType = string(configv1beta1.DeploymentTypeLocal)
}

for j := range list.Items {
r := list.Items[j]
for j := range items {
r := items[j]

// Protective signal, same reasoning as the clustersummary annotation check below:
// skip a resource here when it is explicitly annotated for the *other* deployment
Expand Down Expand Up @@ -1183,6 +1199,33 @@ func processDeployedGVKs(ctx context.Context, isMgmtCluster bool, remoteConfig *
return undeployed, nil
}

// listDeployedResources lists resources of resourceId matching listOptions. The dynamic client
// has no single call to list across only a chosen subset of namespaces, only across all of them
// (used here when the resource is cluster-scoped, or watchNamespaces is empty) or within exactly
// one, so a non-empty watchNamespaces for a namespaced resource is handled by fanning out one
// List per configured namespace and aggregating the results.
func listDeployedResources(ctx context.Context, d dynamic.Interface, resourceId schema.GroupVersionResource,
listOptions *metav1.ListOptions, namespaced bool, watchNamespaces []string) ([]unstructured.Unstructured, error) {

if !namespaced || len(watchNamespaces) == 0 {
list, err := d.Resource(resourceId).List(ctx, *listOptions)
if err != nil {
return nil, err
}
return list.Items, nil
}

items := make([]unstructured.Unstructured, 0)
for _, ns := range watchNamespaces {
list, err := d.Resource(resourceId).Namespace(ns).List(ctx, *listOptions)
if err != nil {
return nil, err
}
items = append(items, list.Items...)
}
return items, nil
}

func undeployStaleResources(ctx context.Context, isMgmtCluster bool,
remoteConfig *rest.Config, remoteClient client.Client, featureID libsveltosv1beta1.FeatureID,
clusterSummary *configv1beta1.ClusterSummary, deployedGVKs []schema.GroupVersionKind,
Expand Down Expand Up @@ -1527,6 +1570,18 @@ func getClusterProfileSpecHash(ctx context.Context, clusterSummary *configv1beta
// the upgrade via ClusterSummary redeployment. v1.0.1 is still added here to make
// sure hash does not change
config += "v1.0.1"

// A change to the operator's agentWatchNamespacesAnnotation on the target Cluster must
// redeploy drift-detection-manager with the new --watch-namespaces value. Without this,
// shouldRedeploy would see an unchanged hash and skip the redeploy entirely, leaving the
// annotation change unapplied until the next periodic drift-detection upgrade sweep.
watchNamespaces, err := getAgentWatchNamespaces(ctx, getManagementClusterClient(),
clusterSummary.Spec.ClusterNamespace, clusterSummary.Spec.ClusterName,
clusterSummary.Spec.ClusterType, logger)
if err != nil {
return "", err
}
config += strings.Join(watchNamespaces, ",")
}

mgmtResourceHash, err := getTemplateResourceRefHash(ctx, clusterSummary)
Expand Down
Loading