diff --git a/build/Dockerfile b/build/Dockerfile index 796d4b46..e6be434a 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -9,7 +9,7 @@ RUN --mount=target=. \ --mount=type=cache,target=/go/pkg \ GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/node-agent . -FROM gcr.io/distroless/static-debian11:latest +FROM gcr.io/distroless/static-debian11:debug COPY --from=builder /out/node-agent /usr/bin/node-agent diff --git a/pkg/applicationprofilemanager/v1/applicationprofile_manager.go b/pkg/applicationprofilemanager/v1/applicationprofile_manager.go index e7a22d5c..c170856a 100644 --- a/pkg/applicationprofilemanager/v1/applicationprofile_manager.go +++ b/pkg/applicationprofilemanager/v1/applicationprofile_manager.go @@ -10,7 +10,7 @@ import ( "node-agent/pkg/k8sclient" "node-agent/pkg/storage" "node-agent/pkg/utils" - "sort" + "os" "time" helpersv1 "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" @@ -36,12 +36,14 @@ type ApplicationProfileManager struct { cfg config.Config clusterName string ctx context.Context - capabilitiesSets maps.SafeMap[string, mapset.Set[string]] // key is k8sContainerID - execMaps maps.SafeMap[string, *maps.SafeMap[string, mapset.Set[string]]] // key is k8sContainerID - openMaps maps.SafeMap[string, *maps.SafeMap[string, mapset.Set[string]]] // key is k8sContainerID + savedCapabilities maps.SafeMap[string, mapset.Set[string]] // key is k8sContainerID + savedExecs maps.SafeMap[string, *maps.SafeMap[string, mapset.Set[string]]] // key is k8sContainerID + savedOpens maps.SafeMap[string, *maps.SafeMap[string, mapset.Set[string]]] // key is k8sContainerID + savedSyscalls maps.SafeMap[string, mapset.Set[string]] // key is k8sContainerID + toSaveCapabilities maps.SafeMap[string, mapset.Set[string]] // key is k8sContainerID + toSaveExecs maps.SafeMap[string, *maps.SafeMap[string, mapset.Set[string]]] // key is k8sContainerID + toSaveOpens maps.SafeMap[string, *maps.SafeMap[string, mapset.Set[string]]] // key is k8sContainerID watchedContainerChannels maps.SafeMap[string, chan error] // key is ContainerID - savedCapabilities maps.SafeMap[string, int] - savedSyscalls maps.SafeMap[string, int] k8sClient k8sclient.K8sClientInterface storageClient storage.StorageClient syscallPeekFunc func(nsMountId uint64) ([]string, error) @@ -114,9 +116,13 @@ func (am *ApplicationProfileManager) ensureInstanceID(ctx context.Context, conta func (am *ApplicationProfileManager) deleteResources(watchedContainer *utils.WatchedContainerData) { watchedContainer.UpdateDataTicker.Stop() - am.capabilitiesSets.Delete(watchedContainer.K8sContainerID) - am.execMaps.Delete(watchedContainer.K8sContainerID) - am.openMaps.Delete(watchedContainer.K8sContainerID) + am.savedCapabilities.Delete(watchedContainer.K8sContainerID) + am.savedExecs.Delete(watchedContainer.K8sContainerID) + am.savedOpens.Delete(watchedContainer.K8sContainerID) + am.savedSyscalls.Delete(watchedContainer.K8sContainerID) + am.toSaveCapabilities.Delete(watchedContainer.K8sContainerID) + am.toSaveExecs.Delete(watchedContainer.K8sContainerID) + am.toSaveOpens.Delete(watchedContainer.K8sContainerID) am.watchedContainerChannels.Delete(watchedContainer.ContainerID) } @@ -142,12 +148,6 @@ func (am *ApplicationProfileManager) monitorContainer(ctx context.Context, conta } } -type PatchOperation struct { - Op string `json:"op"` - Path string `json:"path"` - Value interface{} `json:"value"` -} - func (am *ApplicationProfileManager) saveProfile(ctx context.Context, watchedContainer *utils.WatchedContainerData, namespace string) { ctx, span := otel.Tracer("").Start(ctx, "ApplicationProfileManager.saveProfile") defer span.End() @@ -164,13 +164,17 @@ func (am *ApplicationProfileManager) saveProfile(ctx context.Context, watchedCon return } + // sleep for container index second to desynchronize the profiles saving + time.Sleep(time.Duration(watchedContainer.ContainerIndex) * time.Second) + // get syscalls from IG observedSyscalls, err := am.syscallPeekFunc(watchedContainer.NsMntId) if err != nil { logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to get syscalls", helpers.Error(err)) } // check if we have new activities to save - if len(observedSyscalls) > am.savedSyscalls.Get(watchedContainer.K8sContainerID) { + toSaveSyscalls := mapset.NewSet[string](observedSyscalls...).Difference(am.savedSyscalls.Get(watchedContainer.K8sContainerID)) + if !toSaveSyscalls.IsEmpty() { newActivity := &v1beta1.ApplicationActivity{ ObjectMeta: metav1.ObjectMeta{ Name: slug, @@ -185,163 +189,192 @@ func (am *ApplicationProfileManager) saveProfile(ctx context.Context, watchedCon newActivity.Spec.Syscalls = observedSyscalls // save application activity if err := am.storageClient.CreateApplicationActivity(newActivity, namespace); err != nil { - logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to save application activity", helpers.Error(err)) + logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to save application activity", helpers.Error(err), + helpers.String("slug", slug), + helpers.Int("container index", watchedContainer.ContainerIndex), + helpers.String("container ID", watchedContainer.ContainerID), + helpers.String("k8s workload", watchedContainer.K8sContainerID)) } else { - am.savedSyscalls.Set(watchedContainer.K8sContainerID, len(observedSyscalls)) - logger.L().Debug("ApplicationProfileManager - saved application activity", helpers.String("slug", slug), helpers.String("container ID", watchedContainer.ContainerID), helpers.String("k8s workload", watchedContainer.K8sContainerID)) + am.savedSyscalls.Get(watchedContainer.K8sContainerID).Append(toSaveSyscalls.ToSlice()...) + logger.L().Debug("ApplicationProfileManager - saved application activity", + helpers.Int("syscalls", toSaveSyscalls.Cardinality()), + helpers.String("slug", slug), + helpers.Int("container index", watchedContainer.ContainerIndex), + helpers.String("container ID", watchedContainer.ContainerID), + helpers.String("k8s workload", watchedContainer.K8sContainerID)) } } // profile sets var addedProfiles int - execs := make(map[string]mapset.Set[string]) - opens := make(map[string]mapset.Set[string]) + execsToSave := make(map[string]mapset.Set[string]) + opensToSave := make(map[string]mapset.Set[string]) // get capabilities, execs and opens from IG - var observedCapabilities []string - newCapabilitiesSet := mapset.NewSet[string]() - capabilitiesSet, ok := am.capabilitiesSets.Load(watchedContainer.K8sContainerID) - if ok { - // replace the capabilities set with a new one - am.capabilitiesSets.Set(watchedContainer.K8sContainerID, newCapabilitiesSet) - observedCapabilities = capabilitiesSet.ToSlice() - } - newExecMap := new(maps.SafeMap[string, mapset.Set[string]]) - execMap, ok := am.execMaps.Load(watchedContainer.K8sContainerID) - if ok { - // replace the exec map with a new one - am.execMaps.Set(watchedContainer.K8sContainerID, newExecMap) - // if we fail to save the profile we will restore execMap entries - execMap.Range(func(path string, exec mapset.Set[string]) bool { - if _, exist := execs[path]; !exist { - execs[path] = mapset.NewSet[string]() - } - addedProfiles += execs[path].Append(exec.ToSlice()...) - return true - }) + toSaveCapabilities, ok := am.toSaveCapabilities.Load(watchedContainer.K8sContainerID) + if !ok { + toSaveCapabilities = mapset.NewSet[string]() } - newOpenMap := new(maps.SafeMap[string, mapset.Set[string]]) - openMap, ok := am.openMaps.Load(watchedContainer.K8sContainerID) - if ok { - // replace the open map with a new one - am.openMaps.Set(watchedContainer.K8sContainerID, newOpenMap) - // if we fail to save the profile we will restore openMap entries - openMap.Range(func(path string, open mapset.Set[string]) bool { - if _, exist := opens[path]; !exist { - opens[path] = mapset.NewSet[string]() - } - addedProfiles += opens[path].Append(open.ToSlice()...) - return true - }) + // replace the capabilities set with a new one + am.toSaveCapabilities.Set(watchedContainer.K8sContainerID, mapset.NewSet[string]()) + toSaveExecs, ok := am.toSaveExecs.Load(watchedContainer.K8sContainerID) + if !ok { + toSaveExecs = new(maps.SafeMap[string, mapset.Set[string]]) } - // new profile - if addedProfiles > 0 || len(observedCapabilities) > am.savedCapabilities.Get(watchedContainer.K8sContainerID) { - var profileOperations []PatchOperation - newProfile := &v1beta1.ApplicationProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: slug, - Annotations: map[string]string{ - helpersv1.WlidMetadataKey: watchedContainer.Wlid, - helpersv1.StatusMetadataKey: helpersv1.Ready, - }, - Labels: utils.GetLabels(watchedContainer, true), - }, - } - newProfileContainer := &v1beta1.ApplicationProfileContainer{ - Name: watchedContainer.InstanceID.GetContainerName(), + // replace the exec map with a new one + am.toSaveExecs.Set(watchedContainer.K8sContainerID, new(maps.SafeMap[string, mapset.Set[string]])) + toSaveExecs.Range(func(path string, exec mapset.Set[string]) bool { + if _, exist := execsToSave[path]; !exist { + execsToSave[path] = mapset.NewSet[string]() } - // add capabilities - sort.Strings(observedCapabilities) - newProfileContainer.Capabilities = observedCapabilities - capabilitiesPath := fmt.Sprintf("/spec/containers/%d/capabilities/-", watchedContainer.ContainerIndex) - for _, capability := range observedCapabilities { - profileOperations = append(profileOperations, PatchOperation{ - Op: "add", - Path: capabilitiesPath, - Value: capability, - }) - } - // add execs - newProfileContainer.Execs = make([]v1beta1.ExecCalls, 0) - execsPath := fmt.Sprintf("/spec/containers/%d/execs/-", watchedContainer.ContainerIndex) - for path, exec := range execs { - args := exec.ToSlice() - sort.Strings(args) - newExec := v1beta1.ExecCalls{ - Path: path, - Args: args, - } - newProfileContainer.Execs = append(newProfileContainer.Execs, newExec) - profileOperations = append(profileOperations, PatchOperation{ - Op: "add", - Path: execsPath, - Value: newExec, - }) - } - // add opens - newProfileContainer.Opens = make([]v1beta1.OpenCalls, 0) - opensPath := fmt.Sprintf("/spec/containers/%d/opens/-", watchedContainer.ContainerIndex) - for path, open := range opens { - flags := open.ToSlice() - sort.Strings(flags) - newOpen := v1beta1.OpenCalls{ - Path: path, - Flags: flags, - } - newProfileContainer.Opens = append(newProfileContainer.Opens, newOpen) - profileOperations = append(profileOperations, PatchOperation{ - Op: "add", - Path: opensPath, - Value: newOpen, - }) + addedProfiles += execsToSave[path].Append(exec.ToSlice()...) + return true + }) + toSaveOpens, ok := am.toSaveOpens.Load(watchedContainer.K8sContainerID) + if !ok { + toSaveOpens = new(maps.SafeMap[string, mapset.Set[string]]) + } + // replace the open map with a new one + am.toSaveOpens.Set(watchedContainer.K8sContainerID, new(maps.SafeMap[string, mapset.Set[string]])) + // if we fail to save the profile we will restore openMap entries + toSaveOpens.Range(func(path string, open mapset.Set[string]) bool { + if _, exist := opensToSave[path]; !exist { + opensToSave[path] = mapset.NewSet[string]() } - // insert application profile container - utils.InsertApplicationProfileContainer(newProfile, watchedContainer.ContainerType, watchedContainer.ContainerIndex, newProfileContainer) + addedProfiles += opensToSave[path].Append(open.ToSlice()...) + return true + }) + // new profile activity + if addedProfiles > 0 || !toSaveCapabilities.IsEmpty() { // calculate patch + profileOperations := utils.CreatePatchOperations(toSaveCapabilities.ToSlice(), execsToSave, opensToSave, watchedContainer.ContainerIndex) patch, err := json.Marshal(profileOperations) if err != nil { - logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to marshal patch", helpers.Error(err)) + // FIXME DEBUG log operations + logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to marshal patch", helpers.Error(err), + helpers.Interface("operations", profileOperations), + helpers.String("profile", slug)) return } - // save application profile + // try to patch application profile var gotErr error if err := am.storageClient.PatchApplicationProfile(slug, namespace, patch); err != nil { if apierrors.IsNotFound(err) { + // new application profile + newProfile := &v1beta1.ApplicationProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: slug, + Annotations: map[string]string{ + helpersv1.WlidMetadataKey: watchedContainer.Wlid, + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: utils.GetLabels(watchedContainer, true), + }, + } + // new profile container + newProfileContainer := &v1beta1.ApplicationProfileContainer{ + Name: watchedContainer.InstanceID.GetContainerName(), + } + utils.EnrichProfileContainer(newProfileContainer, toSaveCapabilities.ToSlice(), execsToSave, opensToSave) + // insert application profile container + utils.InsertApplicationProfileContainer(newProfile, watchedContainer.ContainerType, watchedContainer.ContainerIndex, newProfileContainer) + // try to create application profile if err := am.storageClient.CreateApplicationProfile(newProfile, namespace); err != nil { gotErr = err - logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to create application profile", helpers.Error(err)) + logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to create application profile", helpers.Error(err), + helpers.String("slug", slug), + helpers.Int("container index", watchedContainer.ContainerIndex), + helpers.String("container ID", watchedContainer.ContainerID), + helpers.String("k8s workload", watchedContainer.K8sContainerID)) } } else { - gotErr = err - logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to patch application profile", helpers.Error(err)) + logger.L().Ctx(ctx).Debug("ApplicationProfileManager - failed to patch application profile, will get existing one and adjust patch", helpers.Error(err), + helpers.String("slug", slug), + helpers.Int("container index", watchedContainer.ContainerIndex), + helpers.String("container ID", watchedContainer.ContainerID), + helpers.String("k8s workload", watchedContainer.K8sContainerID)) + // get existing profile + existingProfile, err := am.storageClient.GetApplicationProfile(namespace, slug) + if err != nil { + gotErr = err + logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to get existing application profile", helpers.Error(err), + helpers.String("slug", slug), + helpers.Int("container index", watchedContainer.ContainerIndex), + helpers.String("container ID", watchedContainer.ContainerID), + helpers.String("k8s workload", watchedContainer.K8sContainerID)) + } else { + // check existing profile container + op := "replace" + existingProfileContainer := utils.GetApplicationProfileContainer(existingProfile, watchedContainer.ContainerType, watchedContainer.ContainerIndex) + if existingProfileContainer == nil { + op = "add" + existingProfileContainer = &v1beta1.ApplicationProfileContainer{ + Name: watchedContainer.InstanceID.GetContainerName(), + } + } + // update it + utils.EnrichProfileContainer(existingProfileContainer, toSaveCapabilities.ToSlice(), execsToSave, opensToSave) + // replace or add application profile container using patch + replaceOperations := []utils.PatchOperation{{ + Op: op, + Path: fmt.Sprintf("/spec/containers/%d", watchedContainer.ContainerIndex), + Value: existingProfileContainer, + }} + patch, err := json.Marshal(replaceOperations) + if err != nil { + // FIXME DEBUG log operations + logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to marshal patch", helpers.Error(err), + helpers.Interface("operations", profileOperations), + helpers.String("profile", slug)) + return + } + if err := am.storageClient.PatchApplicationProfile(slug, namespace, patch); err != nil { + gotErr = err + logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to patch application profile", helpers.Error(err), + helpers.String("slug", slug), + helpers.Int("container index", watchedContainer.ContainerIndex), + helpers.String("container ID", watchedContainer.ContainerID), + helpers.String("k8s workload", watchedContainer.K8sContainerID)) + // FIXME DEBUG save patch in /tmp + err := os.WriteFile(fmt.Sprintf("/tmp/patch-%d.json", time.Now().UnixNano()), patch, 0644) + if err != nil { + logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed save failed patch to /tmp", helpers.Error(err)) + } + } + } } } if gotErr != nil { // restore capabilities set - newCapabilitiesSet.Append(capabilitiesSet.ToSlice()...) + am.toSaveCapabilities.Get(watchedContainer.K8sContainerID).Append(toSaveCapabilities.ToSlice()...) // restore execMap entries - execMap.Range(func(k string, v mapset.Set[string]) bool { - if newExecMap.Has(k) { - newExecMap.Get(k).Append(v.ToSlice()...) - } else { - newExecMap.Set(k, v) - } - return true - }) + toSaveExecs.Range(utils.RestoreMap(am.toSaveExecs.Get(watchedContainer.K8sContainerID))) // restore openMap entries - openMap.Range(func(k string, v mapset.Set[string]) bool { - if newOpenMap.Has(k) { - newOpenMap.Get(k).Append(v.ToSlice()...) - } else { - newOpenMap.Set(k, v) - } - return true - }) + toSaveOpens.Range(utils.RestoreMap(am.toSaveOpens.Get(watchedContainer.K8sContainerID))) } else { - am.savedCapabilities.Set(watchedContainer.K8sContainerID, len(observedCapabilities)) - logger.L().Debug("ApplicationProfileManager - saved application profile", helpers.String("slug", slug), helpers.String("container ID", watchedContainer.ContainerID), helpers.String("k8s workload", watchedContainer.K8sContainerID)) + // record saved capabilities + am.savedCapabilities.Get(watchedContainer.K8sContainerID).Append(toSaveCapabilities.ToSlice()...) + // record saved execs + toSaveExecs.Range(utils.RestoreMap(am.savedExecs.Get(watchedContainer.K8sContainerID))) + // record saved opens + toSaveOpens.Range(utils.RestoreMap(am.savedOpens.Get(watchedContainer.K8sContainerID))) + logger.L().Debug("ApplicationProfileManager - saved application profile", + helpers.Int("capabilities", toSaveCapabilities.Cardinality()), + helpers.Int("execs", toSaveExecs.Len()), + helpers.Int("opens", toSaveOpens.Len()), + helpers.String("slug", slug), + helpers.Int("container index", watchedContainer.ContainerIndex), + helpers.String("container ID", watchedContainer.ContainerID), + helpers.String("k8s workload", watchedContainer.K8sContainerID)) // profile summary summary := &v1beta1.ApplicationProfileSummary{ - ObjectMeta: newProfile.ObjectMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: slug, + Annotations: map[string]string{ + helpersv1.WlidMetadataKey: watchedContainer.Wlid, + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: utils.GetLabels(watchedContainer, true), + }, } if err := am.storageClient.CreateApplicationProfileSummary(summary, namespace); err != nil { logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to save application profile summary", helpers.Error(err)) @@ -383,9 +416,13 @@ func (am *ApplicationProfileManager) ContainerCallback(notif containercollection logger.L().Debug("container already exist in memory", helpers.String("container ID", notif.Container.Runtime.ContainerID), helpers.String("k8s workload", k8sContainerID)) return } - am.capabilitiesSets.Set(k8sContainerID, mapset.NewSet[string]()) - am.execMaps.Set(k8sContainerID, new(maps.SafeMap[string, mapset.Set[string]])) - am.openMaps.Set(k8sContainerID, new(maps.SafeMap[string, mapset.Set[string]])) + am.savedCapabilities.Set(k8sContainerID, mapset.NewSet[string]()) + am.savedExecs.Set(k8sContainerID, new(maps.SafeMap[string, mapset.Set[string]])) + am.savedOpens.Set(k8sContainerID, new(maps.SafeMap[string, mapset.Set[string]])) + am.savedSyscalls.Set(k8sContainerID, mapset.NewSet[string]()) + am.toSaveCapabilities.Set(k8sContainerID, mapset.NewSet[string]()) + am.toSaveExecs.Set(k8sContainerID, new(maps.SafeMap[string, mapset.Set[string]])) + am.toSaveOpens.Set(k8sContainerID, new(maps.SafeMap[string, mapset.Set[string]])) go am.startApplicationProfiling(ctx, notif.Container, k8sContainerID) case containercollection.EventTypeRemoveContainer: channel := am.watchedContainerChannels.Get(notif.Container.Runtime.ContainerID) @@ -401,7 +438,7 @@ func (am *ApplicationProfileManager) RegisterPeekFunc(peek func(mntns uint64) ([ } func (am *ApplicationProfileManager) ReportCapability(k8sContainerID, capability string) { - capabilitiesSet, err := utils.WaitGetSafeMap(&am.capabilitiesSets, k8sContainerID) + capabilitiesSet, err := utils.WaitGetSafeMap(&am.toSaveCapabilities, k8sContainerID) if err != nil { return } @@ -413,7 +450,7 @@ func (am *ApplicationProfileManager) ReportFileExec(k8sContainerID, path string, if path == "" { return } - execMap, err := utils.WaitGetSafeMap(&am.execMaps, k8sContainerID) + execMap, err := utils.WaitGetSafeMap(&am.toSaveExecs, k8sContainerID) if err != nil { return } @@ -425,7 +462,7 @@ func (am *ApplicationProfileManager) ReportFileExec(k8sContainerID, path string, } func (am *ApplicationProfileManager) ReportFileOpen(k8sContainerID, path string, flags []string) { - openMap, err := utils.WaitGetSafeMap(&am.openMaps, k8sContainerID) + openMap, err := utils.WaitGetSafeMap(&am.toSaveOpens, k8sContainerID) if err != nil { return } diff --git a/pkg/storage/v1/storage_nocache.go b/pkg/storage/v1/storage_nocache.go index 8e523c56..e2799170 100644 --- a/pkg/storage/v1/storage_nocache.go +++ b/pkg/storage/v1/storage_nocache.go @@ -103,6 +103,8 @@ func (sc StorageNoCache) GetApplicationActivity(namespace, name string) (*v1beta } func (sc StorageNoCache) CreateApplicationProfile(profile *v1beta1.ApplicationProfile, namespace string) error { + // unset resourceVersion + profile.ResourceVersion = "" _, err := sc.StorageClient.ApplicationProfiles(namespace).Create(context.Background(), profile, metav1.CreateOptions{}) if err != nil { return err diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 2c82906c..c5e4fb10 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -2,9 +2,12 @@ package utils import ( "errors" + "fmt" + "github.com/deckarep/golang-set/v2" "math/rand" "path/filepath" "runtime" + "sort" "strconv" "strings" "time" @@ -150,6 +153,23 @@ func GetLabels(watchedContainer *WatchedContainerData, stripContainer bool) map[ return labels } +func GetApplicationProfileContainer(profile *v1beta1.ApplicationProfile, containerType ContainerType, containerIndex int) *v1beta1.ApplicationProfileContainer { + if profile == nil { + return nil + } + switch containerType { + case Container: + if len(profile.Spec.Containers) > containerIndex { + return &profile.Spec.Containers[containerIndex] + } + case InitContainer: + if len(profile.Spec.InitContainers) > containerIndex { + return &profile.Spec.InitContainers[containerIndex] + } + } + return nil +} + func InsertApplicationProfileContainer(profile *v1beta1.ApplicationProfile, containerType ContainerType, containerIndex int, profileContainer *v1beta1.ApplicationProfileContainer) { switch containerType { case Container: @@ -206,3 +226,90 @@ func WaitGetSafeMap[K comparable, V any](m *maps.SafeMap[K, V], k K) (V, error) tries++ } } + +func EnrichProfileContainer(newProfileContainer *v1beta1.ApplicationProfileContainer, observedCapabilities []string, execs map[string]mapset.Set[string], opens map[string]mapset.Set[string]) { + // add capabilities + sort.Strings(observedCapabilities) + newProfileContainer.Capabilities = observedCapabilities + // add execs + newProfileContainer.Execs = make([]v1beta1.ExecCalls, 0) + for path, exec := range execs { + args := exec.ToSlice() + sort.Strings(args) + newProfileContainer.Execs = append(newProfileContainer.Execs, v1beta1.ExecCalls{ + Path: path, + Args: args, + }) + } + // add opens + newProfileContainer.Opens = make([]v1beta1.OpenCalls, 0) + for path, open := range opens { + flags := open.ToSlice() + sort.Strings(flags) + newProfileContainer.Opens = append(newProfileContainer.Opens, v1beta1.OpenCalls{ + Path: path, + Flags: flags, + }) + } +} + +type PatchOperation struct { + Op string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value"` +} + +func CreatePatchOperations(observedCapabilities []string, execs map[string]mapset.Set[string], opens map[string]mapset.Set[string], index int) []PatchOperation { + var profileOperations []PatchOperation + // add capabilities + sort.Strings(observedCapabilities) + capabilitiesPath := fmt.Sprintf("/spec/containers/%d/capabilities/-", index) + for _, capability := range observedCapabilities { + profileOperations = append(profileOperations, PatchOperation{ + Op: "add", + Path: capabilitiesPath, + Value: capability, + }) + } + // add execs + execsPath := fmt.Sprintf("/spec/containers/%d/execs/-", index) + for path, exec := range execs { + args := exec.ToSlice() + sort.Strings(args) + profileOperations = append(profileOperations, PatchOperation{ + Op: "add", + Path: execsPath, + Value: v1beta1.ExecCalls{ + Path: path, + Args: args, + }, + }) + } + // add opens + opensPath := fmt.Sprintf("/spec/containers/%d/opens/-", index) + for path, open := range opens { + flags := open.ToSlice() + sort.Strings(flags) + + profileOperations = append(profileOperations, PatchOperation{ + Op: "add", + Path: opensPath, + Value: v1beta1.OpenCalls{ + Path: path, + Flags: flags, + }, + }) + } + return profileOperations +} + +func RestoreMap(newExecMap *maps.SafeMap[string, mapset.Set[string]]) func(k string, v mapset.Set[string]) bool { + return func(k string, v mapset.Set[string]) bool { + if newExecMap.Has(k) { + newExecMap.Get(k).Append(v.ToSlice()...) + } else { + newExecMap.Set(k, v) + } + return true + } +}