Skip to content

Commit

Permalink
add a helper func for setting agent install namespace from addon depl…
Browse files Browse the repository at this point in the history
…oyment config

Signed-off-by: zhujian <jiazhu@redhat.com>
  • Loading branch information
zhujian7 committed Sep 5, 2023
1 parent d5b1f95 commit d5f1aba
Show file tree
Hide file tree
Showing 45 changed files with 1,436 additions and 2,191 deletions.
5 changes: 5 additions & 0 deletions cmd/example/helloworld/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ func runController(ctx context.Context, kubeConfig *rest.Config) error {
utilrand.String(5),
)

// Set agent install namespace from addon deployment config if it exists
registrationOption.AgentInstallNamespace = utils.AgentInstallNamespaceFromDeploymentConfig(
addonfactory.NewAddOnDeploymentConfigGetter(addonClient).Get,
)

agentAddon, err := addonfactory.NewAgentAddonFactory(helloworld.AddonName, helloworld.FS, "manifests/templates").
WithConfigGVRs(utils.AddOnDeploymentConfigGVR).
WithGetValuesFuncs(
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
k8s.io/component-base v0.26.7
k8s.io/klog/v2 v2.80.1
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
open-cluster-management.io/api v0.11.1-0.20230731134013-26e75df27f50
open-cluster-management.io/api v0.11.1-0.20230905055724-cf1ead467a83
sigs.k8s.io/controller-runtime v0.14.4
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -819,8 +819,8 @@ k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+O
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y=
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
open-cluster-management.io/api v0.11.1-0.20230731134013-26e75df27f50 h1:njvnCHeYMQvBWjKW97xa2r3A0/tN8H0FI9kL4AeCqVs=
open-cluster-management.io/api v0.11.1-0.20230731134013-26e75df27f50/go.mod h1:WgKUCJ7+Bf40DsOmH1Gdkpyj3joco+QLzrlM6Ak39zE=
open-cluster-management.io/api v0.11.1-0.20230905055724-cf1ead467a83 h1:3zbT3sT/tEAQbpjIk6uRiTQGknQ3kQlfd11ElVuXyyQ=
open-cluster-management.io/api v0.11.1-0.20230905055724-cf1ead467a83/go.mod h1:nsQ/G5JpfjQUg7dHpblyywWC6BRqklNaF6fIswVCHyY=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
Expand Down
110 changes: 110 additions & 0 deletions pkg/utils/namespace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package utils

import (
"context"
"fmt"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2"
addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
)

func AgentInstallNamespaceFromDeploymentConfig(
adcgetter func(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error),
) func(*addonapiv1alpha1.ManagedClusterAddOn) string {
return func(addon *addonapiv1alpha1.ManagedClusterAddOn) string {
if addon == nil {
utilruntime.HandleError(fmt.Errorf("failed to get addon template, addon is nil"))
return ""
}

config, err := GetDesiredAddOnDeploymentConfig(addon, adcgetter)
if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to get addon %s template: %v", addon.Name, err))
return ""
}
if config == nil {
return ""
}

return config.Spec.AgentInstallNamespace
}
}

// GetDesiredAddOnDeployment returns the desired template of the addon
func GetDesiredAddOnDeploymentConfig(
addon *addonapiv1alpha1.ManagedClusterAddOn,
adcgetter func(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error),
) (*addonapiv1alpha1.AddOnDeploymentConfig, error) {

ok, configRef := GetAddonConfigRef(addon.Status.ConfigReferences,
AddOnDeploymentConfigGVR.Group, AddOnDeploymentConfigGVR.Resource)
if !ok {
klog.InfoS("Addon deployment config in status is empty", "addonName", addon.Name)
return nil, nil
}

desiredConfig := configRef.DesiredConfig
if desiredConfig == nil || len(desiredConfig.SpecHash) == 0 {
klog.InfoS("Addon deployment config spec hash is empty", "addonName", addon.Name)
return nil, fmt.Errorf("addon %s deployment config desired spec hash is empty", addon.Name)
}

adc, err := adcgetter(context.TODO(), desiredConfig.Namespace, desiredConfig.Name)
if err != nil {
return nil, err
}

specHash, err := GetAddOnDeploymentConfigSpecHash(adc)
if err != nil {
return nil, err
}
if specHash != desiredConfig.SpecHash {
return nil, fmt.Errorf("addon %s deployment config spec hash %s is not equal to desired spec hash %s",
addon.Name, specHash, desiredConfig.SpecHash)
}
return adc.DeepCopy(), nil
}

// GetAddOnDeploymentConfigSpecHash returns the sha256 hash of the spec field of the addon deployment config
func GetAddOnDeploymentConfigSpecHash(config *addonapiv1alpha1.AddOnDeploymentConfig) (string, error) {
if config == nil {
return "", fmt.Errorf("addon deployment config is nil")
}
uadc, err := runtime.DefaultUnstructuredConverter.ToUnstructured(config)
if err != nil {
return "", err
}
return GetSpecHash(&unstructured.Unstructured{
Object: uadc,
})
}

// GetAddOnTemplateSpecHash returns the sha256 hash of the spec field of the addon template
func GetAddOnTemplateSpecHash(template *addonapiv1alpha1.AddOnTemplate) (string, error) {
if template == nil {
return "", fmt.Errorf("addon template is nil")
}
uat, err := runtime.DefaultUnstructuredConverter.ToUnstructured(template)
if err != nil {
return "", err
}
return GetSpecHash(&unstructured.Unstructured{
Object: uat,
})
}

// GetAddonConfigRef returns the first addon config ref for the given config type
func GetAddonConfigRef(
configReferences []addonapiv1alpha1.ConfigReference,
group, resource string) (bool, addonapiv1alpha1.ConfigReference) {

for _, config := range configReferences {
if config.Group == group && config.Resource == resource {
return true, config
}
}
return false, addonapiv1alpha1.ConfigReference{}
}
216 changes: 216 additions & 0 deletions pkg/utils/namespace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package utils

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
)

func TestAgentInstallNamespaceFromDeploymentConfig(t *testing.T) {

cases := []struct {
name string
getter func(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error)
mca *addonapiv1alpha1.ManagedClusterAddOn
expected string
}{
{
name: "addon is nil",
getter: func(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error) {
return &addonapiv1alpha1.AddOnDeploymentConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
},
Spec: addonapiv1alpha1.AddOnDeploymentConfigSpec{},
}, nil
},
mca: nil,
expected: "",
},
{
name: "addon no deployment config reference",
getter: func(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error) {
return &addonapiv1alpha1.AddOnDeploymentConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
},
Spec: addonapiv1alpha1.AddOnDeploymentConfigSpec{},
}, nil
},
mca: &addonapiv1alpha1.ManagedClusterAddOn{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "cluster1",
},
Status: addonapiv1alpha1.ManagedClusterAddOnStatus{
ConfigReferences: []addonapiv1alpha1.ConfigReference{},
},
},
expected: "",
},
{
name: "addon deployment config reference spec hash empty",
getter: func(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error) {
return &addonapiv1alpha1.AddOnDeploymentConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
},
Spec: addonapiv1alpha1.AddOnDeploymentConfigSpec{},
}, nil
},
mca: &addonapiv1alpha1.ManagedClusterAddOn{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "cluster1",
},
Status: addonapiv1alpha1.ManagedClusterAddOnStatus{
ConfigReferences: []addonapiv1alpha1.ConfigReference{
{
ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{
Group: "addon.open-cluster-management.io",
Resource: "addondeploymentconfigs",
},
ConfigReferent: addonapiv1alpha1.ConfigReferent{
Name: "test1",
},
},
},
},
},
expected: "",
},
{
name: "addon deployment config reference spec hash not match",
getter: func(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error) {
return &addonapiv1alpha1.AddOnDeploymentConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
},
Spec: addonapiv1alpha1.AddOnDeploymentConfigSpec{
AgentInstallNamespace: "testns",
},
}, nil
},
mca: &addonapiv1alpha1.ManagedClusterAddOn{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "cluster1",
},
Status: addonapiv1alpha1.ManagedClusterAddOnStatus{
ConfigReferences: []addonapiv1alpha1.ConfigReference{
{
ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{
Group: "addon.open-cluster-management.io",
Resource: "addondeploymentconfigs",
},
ConfigReferent: addonapiv1alpha1.ConfigReferent{
Name: "test1",
},
DesiredConfig: &addonapiv1alpha1.ConfigSpecHash{
SpecHash: "wronghash",
},
},
},
},
},
expected: "",
},
{
name: "addon deployment config reference spec hash match",
getter: func(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error) {
return &addonapiv1alpha1.AddOnDeploymentConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
},
Spec: addonapiv1alpha1.AddOnDeploymentConfigSpec{
AgentInstallNamespace: "testns",
},
}, nil
},
mca: &addonapiv1alpha1.ManagedClusterAddOn{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "cluster1",
},
Status: addonapiv1alpha1.ManagedClusterAddOnStatus{
ConfigReferences: []addonapiv1alpha1.ConfigReference{
{
ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{
Group: "addon.open-cluster-management.io",
Resource: "addondeploymentconfigs",
},
ConfigReferent: addonapiv1alpha1.ConfigReferent{
Name: "test1",
},
DesiredConfig: &addonapiv1alpha1.ConfigSpecHash{
SpecHash: "f97b3f6af1f786ec6f3273e2d6fc8717e45cb7bc9797ba7533663a7de84a5538",
},
},
},
},
},
expected: "testns",
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
nsFunc := AgentInstallNamespaceFromDeploymentConfig(c.getter)
ns := nsFunc(c.mca)
assert.Equal(t, c.expected, ns, "should be equal")
})
}
}

func TestGetAddOnTemplateSpecHash(t *testing.T) {
cases := []struct {
name string
at *addonapiv1alpha1.AddOnTemplate
expected string
expectedErr string
}{
{
name: "addon template is nil",
at: nil,
expected: "",
expectedErr: "addon template is nil",
},
{
name: "addon template spec is nil",
at: &addonapiv1alpha1.AddOnTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
},
Spec: addonapiv1alpha1.AddOnTemplateSpec{},
},
expected: "aa3e489402ac2e99c4aef0ddc8cc2fdf1d3b6c34c7b8d040dc3fae5db60478cb",
},
{
name: "addon template spec is not nil",
at: &addonapiv1alpha1.AddOnTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
},
Spec: addonapiv1alpha1.AddOnTemplateSpec{
AddonName: "test1",
},
},
expected: "00730aa8aa1826c9a3cfd8d6858b45e1e16bcdade5cc57070ea8089c6764285e",
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
hash, err := GetAddOnTemplateSpecHash(c.at)
if c.expectedErr != "" {
assert.Equal(t, c.expectedErr, err.Error(), "should be equal")
} else {
assert.Nil(t, err, "should be nil")
}
assert.Equal(t, c.expected, hash, "should be equal")
})
}
}
Loading

0 comments on commit d5f1aba

Please sign in to comment.