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

Add solution scope to target object #616

Merged
merged 8 commits into from
Feb 14, 2025
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
26 changes: 26 additions & 0 deletions api/pkg/apis/v1alpha1/managers/solution/solution-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,13 @@ func (s *SolutionManager) Reconcile(ctx context.Context, deployment model.Deploy
// }
// }

defaultScope := deployment.Instance.Spec.Scope
// ensure to restore the original scope defined in instance in case the scope is changed during the deployment
defer func() {
deployment.Instance.Spec.Scope = defaultScope
}()
for i := 0; i < retryCount; i++ {
deployment.Instance.Spec.Scope = getCurrentApplicationScope(ctx, deployment.Instance, deployment.Targets[step.Target])
componentResults, stepError = (provider.(tgt.ITargetProvider)).Apply(ctx, dep, step, deployment.IsDryRun)
if stepError == nil {
targetResult[step.Target] = 1
Expand All @@ -554,6 +560,7 @@ func (s *SolutionManager) Reconcile(ctx context.Context, deployment model.Deploy
summary.UpdateTargetResult(step.Target, model.TargetResultSpec{Status: targetResultStatus, Message: targetResultMessage, ComponentResults: componentResults}) // TODO: this keeps only the last error on the target
time.Sleep(5 * time.Second) //TODO: make this configurable?
}
deployment.Instance.Spec.Scope = defaultScope
}
if stepError != nil {
log.ErrorfCtx(ctx, " M (Solution): failed to execute deployment step: %+v", stepError)
Expand Down Expand Up @@ -773,6 +780,7 @@ func (s *SolutionManager) Get(ctx context.Context, deployment model.DeploymentSp
ret = state
ret.TargetComponent = make(map[string]string)
retComponents := make([]model.ComponentSpec, 0)
defaultScope := deployment.Instance.Spec.Scope

for _, step := range plan.Steps {
if s.IsTarget && !api_utils.ContainsString(s.TargetNames, step.Target) {
Expand All @@ -783,6 +791,7 @@ func (s *SolutionManager) Get(ctx context.Context, deployment model.DeploymentSp
}

deployment.ActiveTarget = step.Target
deployment.Instance.Spec.Scope = getCurrentApplicationScope(ctx, deployment.Instance, deployment.Targets[step.Target])

var override tgt.ITargetProvider
role := step.Role
Expand Down Expand Up @@ -828,6 +837,7 @@ func (s *SolutionManager) Get(ctx context.Context, deployment model.DeploymentSp
retComponents = append(retComponents, c)
}
}
deployment.Instance.Spec.Scope = defaultScope
}
ret.Components = retComponents
return ret, retComponents, nil
Expand Down Expand Up @@ -888,6 +898,22 @@ func (s *SolutionManager) Reconcil() []error {
return nil
}

func getCurrentApplicationScope(ctx context.Context, instance model.InstanceState, target model.TargetState) string {
log.InfofCtx(ctx, " M (Solution): getting current application scope, instance scope: %s, target application scope: %s", instance.Spec.Scope, target.Spec.SolutionScope)
if instance.Spec.Scope == "" {
if target.Spec.SolutionScope == "" {
return "default"
}
return target.Spec.SolutionScope
}
if target.Spec.SolutionScope != "" && target.Spec.SolutionScope != instance.Spec.Scope {
message := fmt.Sprintf(" M (Solution): target application scope: %s is inconsistent with instance scope: %s", target.Spec.SolutionScope, instance.Spec.Scope)
log.WarnfCtx(ctx, message)
observ_utils.EmitUserAuditsLogs(ctx, message)
}
return instance.Spec.Scope
}

func findAgentFromDeploymentState(state model.DeploymentState, targetName string) string {
for _, targetDes := range state.Targets {
if targetName == targetDes.Name {
Expand Down
5 changes: 5 additions & 0 deletions api/pkg/apis/v1alpha1/model/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type (
TargetSpec struct {
DisplayName string `json:"displayName,omitempty"`
Scope string `json:"scope,omitempty"`
SolutionScope string `json:"solutionScope,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
Properties map[string]string `json:"properties,omitempty"`
Components []ComponentSpec `json:"components,omitempty"`
Expand All @@ -48,6 +49,10 @@ func (c TargetSpec) DeepEquals(other IDeepEquals) (bool, error) {
return false, nil
}

if c.SolutionScope != otherC.SolutionScope {
return false, nil
}

if !StringMapsEqual(c.Metadata, otherC.Metadata, nil) {
return false, nil
}
Expand Down
4 changes: 0 additions & 4 deletions api/pkg/apis/v1alpha1/utils/symphony-api.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,10 +752,6 @@ func CreateSymphonyDeployment(ctx context.Context, instance model.InstanceState,
sTargets[t.ObjectMeta.Name] = t
}

if instance.Spec.Scope == "" {
instance.Spec.Scope = constants.DefaultScope
}

//TODO: handle devices
ret.Solution = solution
ret.Targets = sTargets
Expand Down
1 change: 0 additions & 1 deletion api/pkg/apis/v1alpha1/utils/symphony-api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,6 @@ func TestCreateSymphonyDeployment(t *testing.T) {
},
Spec: &model.InstanceSpec{
Solution: "",
Scope: "default", // CreateSymphonyDeployment will give default if instance.Spec.Scope is empty
Target: model.TargetSelector{
Name: "someTargetName",
Selector: map[string]string{
Expand Down
1 change: 1 addition & 0 deletions k8s/apis/model/v1/common_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type TargetSpec struct {
DisplayName string `json:"displayName,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
Scope string `json:"scope,omitempty"`
SolutionScope string `json:"solutionScope,omitempty"`
Properties map[string]string `json:"properties,omitempty"`
Components []ComponentSpec `json:"components,omitempty"`
Constraints string `json:"constraints,omitempty"`
Expand Down
2 changes: 2 additions & 0 deletions k8s/config/oss/crd/bases/fabric.symphony_targets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ spec:
spec:
description: Defines the desired state of Target
properties:
solutionScope:
type: string
components:
items:
description: Defines a desired runtime component
Expand Down
1 change: 1 addition & 0 deletions k8s/utils/symphony-api.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func K8STargetToAPITargetState(target fabric_v1.Target) (apimodel.TargetState, e
DisplayName: target.Spec.DisplayName,
Metadata: target.Spec.Metadata,
Scope: target.Spec.Scope,
SolutionScope: target.Spec.SolutionScope,
Properties: target.Spec.Properties,
Constraints: target.Spec.Constraints,
ForceRedeploy: target.Spec.ForceRedeploy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1684,6 +1684,8 @@ spec:
spec:
description: Defines the desired state of Target
properties:
solutionScope:
type: string
components:
items:
description: Defines a desired runtime component
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: solution.symphony/v1
kind: Instance
metadata:
name: instance-configmap
spec:
solution: solution-configmap:v1
target:
name: target-configmap
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: solution.symphony/v1
kind: Instance
metadata:
name: instance-configmap
spec:
scope: nondefault
solution: solution-configmap:v1
target:
name: target-configmap
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: solution.symphony/v1
kind: SolutionContainer
metadata:
name: solution-configmap
spec:
---
apiVersion: solution.symphony/v1
kind: Solution
metadata:
name: solution-configmap-v-v1
spec:
rootResource: solution-configmap
components:
- name: configmap
type: config
properties:
tags: "test-tag"
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: fabric.symphony/v1
kind: Target
metadata:
name: target-configmap
spec:
topologies:
- bindings:
- role: config
provider: providers.target.configmap
config:
inCluster: "true"
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: fabric.symphony/v1
kind: Target
metadata:
name: target-configmap
spec:
solutionScope: target-scope
topologies:
- bindings:
- role: config
provider: providers.target.configmap
config:
inCluster: "true"
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,86 @@ func TestBasic_VerifySameInstanceRecreationInNamespace(t *testing.T) {
}
}

func TestBasic_VerifyTargetSolutionScope(t *testing.T) {
// Manifests to deploy
var testManifests = []string{
"../manifest/oss/solution-configmap.yaml",
"../manifest/oss/target-configmap-default.yaml",
"../manifest/oss/instance-configmap-default.yaml",
}

// Deploy the manifests in default namespace
for _, manifest := range testManifests {
fullPath, err := filepath.Abs(manifest)
require.NoError(t, err)

err = shellcmd.Command(fmt.Sprintf("kubectl apply -f %s -n default", fullPath)).Run()
require.NoError(t, err)
}

cfg, err := testhelpers.RestConfig()
require.NoError(t, err)
clientset, err := kubernetes.NewForConfig(cfg)
require.NoError(t, err)

// Verify configmap in default scope
for {
namespace := "default"
configMapName := "configmap"
configMap, err := clientset.CoreV1().ConfigMaps(namespace).Get(context.Background(), configMapName, metav1.GetOptions{})
if err == nil {
require.Equal(t, "test-tag", configMap.Data["tags"], "configmap data should match the input")
break
}

sleepDuration, _ := time.ParseDuration("5s")
time.Sleep(sleepDuration)
}

// update target with solutionScope
targetFile := "../manifest/oss/target-configmap.yaml"
fullPath, err := filepath.Abs(targetFile)
require.NoError(t, err)
err = shellcmd.Command(fmt.Sprintf("kubectl apply -f %s -n default", fullPath)).Run()
require.NoError(t, err)

// Verify configmp in target solutionScope
for {
namespace := "target-scope"
configMapName := "configmap"
configMap, err := clientset.CoreV1().ConfigMaps(namespace).Get(context.Background(), configMapName, metav1.GetOptions{})
if err == nil {
require.Equal(t, "test-tag", configMap.Data["tags"], "configmap data should match the input")
break
}

sleepDuration, _ := time.ParseDuration("5s")
time.Sleep(sleepDuration)
}

// Update instance scope with nondefault namespace
instanceFile := "../manifest/oss/instance-configmap-with-scope.yaml"
fullPath, err = filepath.Abs(instanceFile)
require.NoError(t, err)
err = shellcmd.Command(fmt.Sprintf("kubectl apply -f %s -n default", fullPath)).Run()
require.NoError(t, err)

// Verify configmap in nondefault instance scope
for {
namespace := "nondefault"
configMapName := "configmap"
configMap, err := clientset.CoreV1().ConfigMaps(namespace).Get(context.Background(), configMapName, metav1.GetOptions{})
if err == nil {
require.Equal(t, "test-tag", configMap.Data["tags"], "configmap data should match the input")
break
}

sleepDuration, _ := time.ParseDuration("5s")
time.Sleep(sleepDuration)
}

}

// Helper for read catalog
func readCatalog(catalogName string, namespace string, dynamicClient dynamic.Interface) (*unstructured.Unstructured, error) {
gvr := schema.GroupVersionResource{Group: "federation.symphony", Version: "v1", Resource: "catalogs"}
Expand Down
Loading