Skip to content

Commit a6f85d6

Browse files
authored
Merge pull request #652 from chifu1234/kkl-dev
feat: add subressource patch
2 parents 9c6c252 + 4781463 commit a6f85d6

File tree

4 files changed

+107
-14
lines changed

4 files changed

+107
-14
lines changed

controllers/handlers_helm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2096,7 +2096,7 @@ func addExtraMetadata(ctx context.Context, requestedChart *configv1beta1.HelmCha
20962096
addExtraLabels(r, clusterSummary.Spec.ClusterProfileSpec.ExtraLabels)
20972097
addExtraAnnotations(r, clusterSummary.Spec.ClusterProfileSpec.ExtraAnnotations)
20982098

2099-
err = updateResource(ctx, dr, clusterSummary, r, logger)
2099+
err = updateResource(ctx, dr, clusterSummary, r, []string{}, logger)
21002100
if err != nil {
21012101
logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to update resource %s %s/%s: %v",
21022102
r.GetKind(), r.GetNamespace(), r.GetName(), err))

controllers/handlers_kustomize.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ func deployKustomizeResources(ctx context.Context, c client.Client, remoteRestCo
707707
Name: kustomizationRef.Name,
708708
}
709709
localReports, err = deployUnstructured(ctx, true, localConfig, c, objectsToDeployLocally,
710-
ref, configv1beta1.FeatureKustomize, clusterSummary, mgmtResources, logger)
710+
ref, configv1beta1.FeatureKustomize, clusterSummary, mgmtResources, []string{}, logger)
711711
if err != nil {
712712
logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to deploy to management cluster %v", err))
713713
return localReports, nil, err
@@ -719,7 +719,7 @@ func deployKustomizeResources(ctx context.Context, c client.Client, remoteRestCo
719719
}
720720

721721
remoteReports, err = deployUnstructured(ctx, false, remoteRestConfig, remoteClient, objectsToDeployRemotely,
722-
ref, configv1beta1.FeatureKustomize, clusterSummary, mgmtResources, logger)
722+
ref, configv1beta1.FeatureKustomize, clusterSummary, mgmtResources, []string{}, logger)
723723
if err != nil {
724724
logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to deploy to remote cluster %v", err))
725725
return localReports, remoteReports, err

controllers/handlers_utils.go

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ const (
6464
separator = "---\n"
6565
reasonLabel = "projectsveltos.io/reason"
6666
clusterSummaryAnnotation = "projectsveltos.io/clustersummary"
67+
subresourcesAnnotation = "projectsveltos.io/subresources"
6768
pathAnnotation = "path"
6869
)
6970

@@ -202,18 +203,34 @@ func readFiles(dir string) (map[string]string, error) {
202203
// updateResource creates or updates a resource in a CAPI Cluster.
203204
// No action in DryRun mode.
204205
func updateResource(ctx context.Context, dr dynamic.ResourceInterface,
205-
clusterSummary *configv1beta1.ClusterSummary, object *unstructured.Unstructured,
206+
clusterSummary *configv1beta1.ClusterSummary, object *unstructured.Unstructured, subresources []string,
206207
logger logr.Logger) error {
207208

208209
// No-op in DryRun mode
209210
if clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun {
210211
return nil
211212
}
212213

213-
l := logger.WithValues("resourceNamespace", object.GetNamespace(),
214-
"resourceName", object.GetName(), "resourceGVK", object.GetObjectKind().GroupVersionKind())
214+
l := logger.WithValues("resourceNamespace", object.GetNamespace(), "resourceName", object.GetName(),
215+
"resourceGVK", object.GetObjectKind().GroupVersionKind(), "subresources", subresources)
215216
l.V(logs.LogDebug).Info("deploying policy")
216217

218+
if len(subresources) > 0 {
219+
// Patch without subresources. This will make sure metadata and spec are eventually updated
220+
err := patchRessource(ctx, dr, object, nil)
221+
if err != nil {
222+
return err
223+
}
224+
}
225+
// Reset resource version in case of subresources a patch has already been made
226+
// and that alters the resourceVersion
227+
object.SetResourceVersion("")
228+
return patchRessource(ctx, dr, object, subresources)
229+
}
230+
231+
func patchRessource(ctx context.Context, dr dynamic.ResourceInterface,
232+
object *unstructured.Unstructured, subresources []string) error {
233+
217234
data, err := runtime.Encode(unstructured.UnstructuredJSONScheme, object)
218235
if err != nil {
219236
return err
@@ -224,7 +241,7 @@ func updateResource(ctx context.Context, dr dynamic.ResourceInterface,
224241
FieldManager: "application/apply-patch",
225242
Force: &forceConflict,
226243
}
227-
_, err = dr.Patch(ctx, object.GetName(), types.ApplyPatchType, data, options)
244+
_, err = dr.Patch(ctx, object.GetName(), types.ApplyPatchType, data, options, subresources...)
228245
return err
229246
}
230247

@@ -241,6 +258,18 @@ func instantiateTemplate(referencedObject client.Object, logger logr.Logger) boo
241258
return false
242259
}
243260

261+
func getSubresources(referencedObject client.Object) []string {
262+
annotations := referencedObject.GetAnnotations()
263+
if annotations != nil {
264+
value, exists := annotations[subresourcesAnnotation]
265+
if exists {
266+
subresources := strings.Split(value, ",")
267+
return subresources
268+
}
269+
}
270+
return nil
271+
}
272+
244273
// deployContent deploys policies contained in a ConfigMap/Secret.
245274
// data might have one or more keys. Each key might contain a single policy
246275
// or multiple policies separated by '---'
@@ -252,6 +281,7 @@ func deployContent(ctx context.Context, deployingToMgmtCluster bool, destConfig
252281
mgmtResources map[string]*unstructured.Unstructured, logger logr.Logger,
253282
) (reports []configv1beta1.ResourceReport, err error) {
254283

284+
subresources := getSubresources(referencedObject)
255285
instantiateTemplate := instantiateTemplate(referencedObject, logger)
256286
resources, err := collectContent(ctx, clusterSummary, mgmtResources, data, instantiateTemplate, logger)
257287
if err != nil {
@@ -265,7 +295,7 @@ func deployContent(ctx context.Context, deployingToMgmtCluster bool, destConfig
265295
}
266296

267297
return deployUnstructured(ctx, deployingToMgmtCluster, destConfig, destClient, resources, ref,
268-
configv1beta1.FeatureResources, clusterSummary, mgmtResources, logger)
298+
configv1beta1.FeatureResources, clusterSummary, mgmtResources, subresources, logger)
269299
}
270300

271301
// adjustNamespace fixes namespace.
@@ -298,8 +328,8 @@ func adjustNamespace(policy *unstructured.Unstructured, destConfig *rest.Config)
298328
//nolint:funlen // requires a lot of arguments because kustomize and plain resources are using this function
299329
func deployUnstructured(ctx context.Context, deployingToMgmtCluster bool, destConfig *rest.Config,
300330
destClient client.Client, referencedUnstructured []*unstructured.Unstructured, referencedObject *corev1.ObjectReference,
301-
featureID configv1beta1.FeatureID, clusterSummary *configv1beta1.ClusterSummary, mgmtResources map[string]*unstructured.Unstructured, logger logr.Logger,
302-
) (reports []configv1beta1.ResourceReport, err error) {
331+
featureID configv1beta1.FeatureID, clusterSummary *configv1beta1.ClusterSummary, mgmtResources map[string]*unstructured.Unstructured,
332+
subresources []string, logger logr.Logger) (reports []configv1beta1.ResourceReport, err error) {
303333

304334
profile, profileTier, err := configv1beta1.GetProfileOwnerAndTier(ctx, getManagementClusterClient(), clusterSummary)
305335
if err != nil {
@@ -343,9 +373,6 @@ func deployUnstructured(ctx context.Context, deployingToMgmtCluster bool, destCo
343373
return nil, err
344374
}
345375

346-
// If policy already exists, just get current version and update it by overridding
347-
// all metadata and spec.
348-
// If policy does not exist already, create it
349376
dr, err := utils.GetDynamicResourceInterface(destConfig, policy.GroupVersionKind(), policy.GetNamespace())
350377
if err != nil {
351378
return nil, err
@@ -393,7 +420,7 @@ func deployUnstructured(ctx context.Context, deployingToMgmtCluster bool, destCo
393420
}
394421
}
395422

396-
err = updateResource(ctx, dr, clusterSummary, policy, logger)
423+
err = updateResource(ctx, dr, clusterSummary, policy, subresources, logger)
397424
if err != nil {
398425
return reports, err
399426
}

controllers/handlers_utils_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3434
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3535
"k8s.io/apimachinery/pkg/types"
36+
"k8s.io/apimachinery/pkg/util/intstr"
3637
"k8s.io/client-go/util/retry"
3738
"k8s.io/klog/v2/textlogger"
3839
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -1317,6 +1318,71 @@ stringData:
13171318
Expect(err).To(BeNil())
13181319
Expect(len(u)).To(Equal(3))
13191320
})
1321+
1322+
It("patchRessource with subresources correctly update instance", func() {
1323+
serviceName := randomString()
1324+
key := randomString()
1325+
value := randomString()
1326+
servicePatch := `apiVersion: v1
1327+
kind: Service
1328+
metadata:
1329+
name: %s
1330+
namespace: default
1331+
labels:
1332+
%s: %s
1333+
spec:
1334+
selector:
1335+
%s: %s
1336+
status:
1337+
loadBalancer:
1338+
ingress:
1339+
- ip: 1.1.1.1`
1340+
1341+
service := &corev1.Service{
1342+
ObjectMeta: metav1.ObjectMeta{
1343+
Name: serviceName,
1344+
Namespace: "default",
1345+
},
1346+
Spec: corev1.ServiceSpec{
1347+
Type: corev1.ServiceTypeLoadBalancer,
1348+
Selector: map[string]string{
1349+
"app.kubernetes.io/name": "service0",
1350+
},
1351+
Ports: []corev1.ServicePort{
1352+
{
1353+
Protocol: "TCP",
1354+
Port: 80,
1355+
TargetPort: intstr.FromInt(1234),
1356+
},
1357+
},
1358+
},
1359+
}
1360+
1361+
Expect(testEnv.Client.Create(context.TODO(), service)).To(Succeed())
1362+
Expect(waitForObject(ctx, testEnv.Client, service)).To(Succeed())
1363+
Expect(addTypeInformationToObject(testEnv.Scheme(), clusterSummary)).To(Succeed())
1364+
1365+
configMap := createConfigMapWithPolicy(namespace, randomString(), fmt.Sprintf(servicePatch,
1366+
serviceName, key, value, key, value))
1367+
configMap.Annotations = map[string]string{
1368+
"projectsveltos.io/subresources": "status"}
1369+
_, err := controllers.DeployContentOfConfigMap(context.TODO(), false, testEnv.Config, testEnv.Client,
1370+
configMap, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig()))
1371+
Expect(err).To(BeNil())
1372+
1373+
serviceOut := corev1.Service{}
1374+
Expect(testEnv.Client.Get(context.TODO(),
1375+
types.NamespacedName{Namespace: "default", Name: serviceName}, &serviceOut)).To(Succeed())
1376+
1377+
// verify status has been updated
1378+
Expect(serviceOut.Status.LoadBalancer.Ingress[0].IP).To(Equal("1.1.1.1"))
1379+
// verify metadata has been updated
1380+
Expect(serviceOut.Labels).To(Not(BeNil()))
1381+
Expect(serviceOut.Labels[key]).To(Equal(value))
1382+
// verify spec has been updated
1383+
Expect(serviceOut.Spec.Selector).To(Not(BeNil()))
1384+
Expect(serviceOut.Spec.Selector[key]).To(Equal(value))
1385+
})
13201386
})
13211387

13221388
// validateResourceReports validates that number of resourceResources with certain actions

0 commit comments

Comments
 (0)