Skip to content

Commit 026763e

Browse files
committed
Auto deploy dependencie: enable automatic deployment of prerequisite profile
Profile instances can declare dependencies on other profiles via the DependsOn field, establishing a directed acyclic graph (DAG) of dependencies. Before this PR, it was the administrator's responsibility to ensure that both the dependent and prerequisite profiles target the same set of managed clusters through matching cluster selectors. With this PR, Sveltos will automatically resolve and deploy the prerequisite profiles specified in the DependsOn field. Sveltos will analyze the dependency graph, identify the required prerequisite profiles, and ensure they are deployed to the same clusters. This introduce a behavior change. Set the addon-controller arg `auto-deploy-dependencies` to disable it.
1 parent 6641cf4 commit 026763e

22 files changed

Lines changed: 1259 additions & 125 deletions

api/v1beta1/status.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,8 @@ type Status struct {
3737
// Spec
3838
// +optional
3939
UpdatedClusters Clusters `json:"updatedClusters,omitempty"`
40+
41+
// DependenciesHash is a hash representing the set of clusters where this ClusterProfile
42+
// must be deployed, based on the combined configuration of its dependencies.
43+
DependenciesHash []byte `json:"dependenciesHash,omitempty"`
4044
}

api/v1beta1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/main.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import (
5959
configv1beta1 "github.com/projectsveltos/addon-controller/api/v1beta1"
6060
"github.com/projectsveltos/addon-controller/api/v1beta1/index"
6161
"github.com/projectsveltos/addon-controller/controllers"
62+
"github.com/projectsveltos/addon-controller/controllers/dependencymanager"
6263
"github.com/projectsveltos/addon-controller/internal/telemetry"
6364
libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1"
6465
"github.com/projectsveltos/libsveltos/lib/crd"
@@ -91,6 +92,7 @@ var (
9192
capiOnboardAnnotation string
9293
disableCaching bool
9394
disableTelemetry bool
95+
autoDeployDependencies bool
9496
)
9597

9698
const (
@@ -177,6 +179,8 @@ func main() {
177179
controllers.SetDriftdetectionConfigMap(driftDetectionConfigMap)
178180
controllers.SetLuaConfigMap(luaConfigMap)
179181
controllers.SetCAPIOnboardAnnotation(capiOnboardAnnotation)
182+
// Start dependency manager
183+
dependencymanager.InitializeManagerInstance(ctx, mgr.GetClient(), autoDeployDependencies, ctrl.Log.WithName("dependency_manager"))
180184

181185
logsettings.RegisterForLogSettings(ctx,
182186
libsveltosv1beta1.ComponentAddonManager, ctrl.Log.WithName("log-setter"),
@@ -277,6 +281,27 @@ func initFlags(fs *pflag.FlagSet) {
277281
fs.DurationVar(&conflictRetryTime, "conflict-retry-time", defaultConflictRetryTime*time.Second,
278282
fmt.Sprintf("The minimum interval at which watched ClusterProfile with conflicts are retried. Defaul: %d seconds",
279283
defaultConflictRetryTime))
284+
285+
// AutoDeployDependencies enables automatic deployment of prerequisite profiles.
286+
//
287+
// Profile instances can specify dependencies on other profiles using the
288+
// DependsOn field, forming a directed acyclic graph (DAG) of dependencies.
289+
//
290+
// When AutoDeployDependencies is set to true, Sveltos automatically resolves and deploys
291+
// the prerequisite profiles listed in the DependsOn field. This automation
292+
// ensures that all required dependencies are deployed to the same managed clusters
293+
// as the dependent profile. Sveltos analyzes the dependency graph to identify
294+
// and deploy prerequisites in the correct order.
295+
//
296+
// By default, AutoDeployDependencies is enabled (true). When disabled, administrators
297+
// are responsible for ensuring that both the dependent and prerequisite profiles
298+
// target the same set of managed clusters through matching cluster selectors.
299+
//
300+
// Enabling AutoDeployDependencies simplifies multi-cluster management by automating
301+
// dependency resolution, reducing manual effort, and minimizing the risk of
302+
// configuration inconsistencies.
303+
fs.BoolVar(&autoDeployDependencies, "auto-deploy-dependencies", true,
304+
" When AutoDeployDependencies is set to true, Sveltos will automatically resolve and deploy the prerequisite profiles specified in the DependsOn field")
280305
}
281306

282307
func setupIndexes(ctx context.Context, mgr ctrl.Manager) {

config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,12 @@ spec:
10581058
status:
10591059
description: Status defines the observed state of ClusterProfile/Profile
10601060
properties:
1061+
dependenciesHash:
1062+
description: |-
1063+
DependenciesHash is a hash representing the set of clusters where this ClusterProfile
1064+
must be deployed, based on the combined configuration of its dependencies.
1065+
format: byte
1066+
type: string
10611067
matchingClusters:
10621068
description: |-
10631069
MatchingClusterRefs reference all the clusters currently matching

config/crd/bases/config.projectsveltos.io_profiles.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,12 @@ spec:
10581058
status:
10591059
description: Status defines the observed state of ClusterProfile/Profile
10601060
properties:
1061+
dependenciesHash:
1062+
description: |-
1063+
DependenciesHash is a hash representing the set of clusters where this ClusterProfile
1064+
must be deployed, based on the combined configuration of its dependencies.
1065+
format: byte
1066+
type: string
10611067
matchingClusters:
10621068
description: |-
10631069
MatchingClusterRefs reference all the clusters currently matching

controllers/clusterprofile_controller.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"sigs.k8s.io/controller-runtime/pkg/source"
3939

4040
configv1beta1 "github.com/projectsveltos/addon-controller/api/v1beta1"
41+
"github.com/projectsveltos/addon-controller/controllers/dependencymanager"
4142
"github.com/projectsveltos/addon-controller/pkg/scope"
4243
libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1"
4344
logs "github.com/projectsveltos/libsveltos/lib/logsettings"
@@ -172,6 +173,15 @@ func (r *ClusterProfileReconciler) reconcileNormal(
172173
}
173174
matchingCluster = append(matchingCluster, clusterSetClusters...)
174175

176+
// Get all clusters where deployment is required based on dependent ClusterProfile instances
177+
depManager, err := dependencymanager.GetManagerInstance()
178+
if err != nil {
179+
return reconcile.Result{Requeue: true, RequeueAfter: normalRequeueAfter}
180+
}
181+
profileRef := getKeyFromObject(r.Scheme, profileScope.Profile)
182+
depClusters := depManager.GetClusterDeployments(profileRef)
183+
matchingCluster = append(matchingCluster, depClusters...)
184+
175185
profileScope.SetMatchingClusterRefs(removeDuplicates(matchingCluster))
176186

177187
r.updateMaps(profileScope)
Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +0,0 @@
1-
apiVersion: apps/v1
2-
kind: Deployment
3-
metadata:
4-
name: my-depl
5-
namespace: foo
6-
labels:
7-
app: my-app
8-
spec:
9-
replicas: 3
10-
selector:
11-
matchLabels:
12-
app: my-app
13-
template:
14-
metadata:
15-
labels:
16-
app: my-app
17-
spec:
18-
containers:
19-
- name: my-container
20-
image: hello-world
21-
---
22-
apiVersion: autoscaling/v1
23-
kind: HorizontalPodAutoscaler
24-
metadata:
25-
name: my-autoscaler
26-
namespace: foo
27-
spec:
28-
scaleTargetRef:
29-
apiVersion: apps/v1
30-
kind: Deployment
31-
name: not-my-depl
32-
minReplicas: 1
33-
maxReplicas: 5
34-
targetCPUUtilizationPercentage: 80
Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +0,0 @@
1-
# This Lua policies validate that in namespace "foo"
2-
# each deployment has an associated HorizontalPodAutoscaler
3-
4-
function evaluate()
5-
local hs = {}
6-
hs.valid = true
7-
hs.message = ""
8-
9-
local deployments = {}
10-
local autoscalers = {}
11-
12-
-- Separate deployments and services from the resources
13-
for _, resource in ipairs(resources) do
14-
local kind = resource.kind
15-
if resource.metadata.namespace == "foo" then
16-
if kind == "Deployment" then
17-
table.insert(deployments, resource)
18-
elseif kind == "HorizontalPodAutoscaler" then
19-
table.insert(autoscalers, resource)
20-
end
21-
end
22-
end
23-
24-
-- Check for each deployment if there is a matching HorizontalPodAutoscaler
25-
for _, deployment in ipairs(deployments) do
26-
local deploymentName = deployment.metadata.name
27-
local matchingAutoscaler = false
28-
29-
for _, autoscaler in ipairs(autoscalers) do
30-
if autoscaler.spec.scaleTargetRef.name == deployment.metadata.name then
31-
matchingAutoscaler = true
32-
break
33-
end
34-
end
35-
36-
if not matchingAutoscaler then
37-
hs.valid = false
38-
hs.message = "No matching autoscaler found for deployment: " .. deploymentName
39-
break
40-
end
41-
end
42-
43-
return hs
44-
end
Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +0,0 @@
1-
apiVersion: apps/v1
2-
kind: Deployment
3-
metadata:
4-
name: my-depl
5-
namespace: foo
6-
labels:
7-
app: my-app
8-
spec:
9-
replicas: 3
10-
selector:
11-
matchLabels:
12-
app: my-app
13-
template:
14-
metadata:
15-
labels:
16-
app: my-app
17-
spec:
18-
containers:
19-
- name: my-container
20-
image: hello-world
21-
---
22-
apiVersion: autoscaling/v1
23-
kind: HorizontalPodAutoscaler
24-
metadata:
25-
name: my-autoscaler
26-
namespace: foo
27-
spec:
28-
scaleTargetRef:
29-
apiVersion: apps/v1
30-
kind: Deployment
31-
name: my-depl
32-
minReplicas: 1
33-
maxReplicas: 5
34-
targetCPUUtilizationPercentage: 80

controllers/controllers_suite_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838

3939
"github.com/projectsveltos/addon-controller/api/v1beta1/index"
4040
"github.com/projectsveltos/addon-controller/controllers"
41+
"github.com/projectsveltos/addon-controller/controllers/dependencymanager"
4142
"github.com/projectsveltos/addon-controller/internal/test/helpers"
4243
libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1"
4344
"github.com/projectsveltos/libsveltos/lib/clusterproxy"
@@ -150,6 +151,8 @@ var _ = BeforeSuite(func() {
150151
_, err = clusterproxy.GetListOfClusters(context.TODO(), testEnv.Client, "", "",
151152
textlogger.NewLogger(textlogger.NewConfig()))
152153
Expect(err).To(BeNil())
154+
155+
dependencymanager.InitializeManagerInstance(context.TODO(), testEnv.Client, false, ctrl.Log.WithName("controller_test_suite"))
153156
})
154157

155158
var _ = AfterSuite(func() {

0 commit comments

Comments
 (0)