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 6, 2023
1 parent fa8ed07 commit 171db9a
Show file tree
Hide file tree
Showing 46 changed files with 1,443 additions and 2,221 deletions.
7 changes: 6 additions & 1 deletion cmd/example/helloworld/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,17 @@ 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.AgentInstallNamespaceFromDeploymentConfigFunc(
utils.NewAddOnDeploymentConfigGetter(addonClient),
)

agentAddon, err := addonfactory.NewAgentAddonFactory(helloworld.AddonName, helloworld.FS, "manifests/templates").
WithConfigGVRs(utils.AddOnDeploymentConfigGVR).
WithGetValuesFuncs(
helloworld.GetDefaultValues,
addonfactory.GetAddOnDeploymentConfigValues(
addonfactory.NewAddOnDeploymentConfigGetter(addonClient),
utils.NewAddOnDeploymentConfigGetter(addonClient),
addonfactory.ToAddOnDeploymentConfigValues,
addonfactory.ToImageOverrideValuesFunc("Image", helloworld.DefaultHelloWorldExampleImage),
),
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
38 changes: 9 additions & 29 deletions pkg/addonfactory/addondeploymentconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"strings"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
Expand All @@ -29,25 +28,10 @@ var AddOnDeploymentConfigGVR = schema.GroupVersionResource{
// Deprecated: use AddOnDeploymentConfigToValuesFunc instead.
type AddOnDeloymentConfigToValuesFunc func(config addonapiv1alpha1.AddOnDeploymentConfig) (Values, error)

// AddOnDeloymentConfigGetter has a method to return a AddOnDeploymentConfig object
// Deprecated: use AddOnDeploymentConfigGetter instead.
type AddOnDeloymentConfigGetter interface {
Get(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error)
}

type defaultAddOnDeploymentConfigGetter struct {
addonClient addonv1alpha1client.Interface
}

func (g *defaultAddOnDeploymentConfigGetter) Get(
ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error) {
return g.addonClient.AddonV1alpha1().AddOnDeploymentConfigs(namespace).Get(ctx, name, metav1.GetOptions{})
}

// NewAddOnDeloymentConfigGetter returns a AddOnDeloymentConfigGetter with addon client
// Deprecated: use NewAddOnDeploymentConfigGetter instead.
func NewAddOnDeloymentConfigGetter(addonClient addonv1alpha1client.Interface) AddOnDeloymentConfigGetter {
return &defaultAddOnDeploymentConfigGetter{addonClient: addonClient}
// Deprecated: use NewAddOnDeploymentConfigGetter in pkg/utils package instead.
func NewAddOnDeloymentConfigGetter(addonClient addonv1alpha1client.Interface) utils.AddOnDeploymentConfigGetter {
return utils.NewAddOnDeploymentConfigGetter(addonClient)
}

// GetAddOnDeloymentConfigValues uses AddOnDeloymentConfigGetter to get the AddOnDeploymentConfig object, then
Expand All @@ -56,7 +40,7 @@ func NewAddOnDeloymentConfigGetter(addonClient addonv1alpha1client.Interface) Ad
// override the one from small index
// Deprecated: use GetAddOnDeploymentConfigValues instead.
func GetAddOnDeloymentConfigValues(
getter AddOnDeloymentConfigGetter, toValuesFuncs ...AddOnDeloymentConfigToValuesFunc) GetValuesFunc {
getter utils.AddOnDeploymentConfigGetter, toValuesFuncs ...AddOnDeloymentConfigToValuesFunc) GetValuesFunc {
return func(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn) (Values, error) {
var lastValues = Values{}
for _, config := range addon.Status.ConfigReferences {
Expand Down Expand Up @@ -212,22 +196,18 @@ func ToAddOnCustomizedVariableValues(config addonapiv1alpha1.AddOnDeploymentConf
// The transformation logic depends on the definition of the addon template
type AddOnDeploymentConfigToValuesFunc func(config addonapiv1alpha1.AddOnDeploymentConfig) (Values, error)

// AddOnDeploymentConfigGetter has a method to return a AddOnDeploymentConfig object
type AddOnDeploymentConfigGetter interface {
Get(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error)
}

// NewAddOnDeploymentConfigGetter returns a AddOnDeploymentConfigGetter with addon client
func NewAddOnDeploymentConfigGetter(addonClient addonv1alpha1client.Interface) AddOnDeploymentConfigGetter {
return &defaultAddOnDeploymentConfigGetter{addonClient: addonClient}
// Deprecated: use NewAddOnDeploymentConfigGetter in pkg/utils package instead.
func NewAddOnDeploymentConfigGetter(addonClient addonv1alpha1client.Interface) utils.AddOnDeploymentConfigGetter {
return utils.NewAddOnDeploymentConfigGetter(addonClient)
}

// GetAddOnDeploymentConfigValues uses AddOnDeploymentConfigGetter to get the AddOnDeploymentConfig object, then
// uses AddOnDeploymentConfigToValuesFunc to transform the AddOnDeploymentConfig object to Values object
// If there are multiple AddOnDeploymentConfig objects in the AddOn ConfigReferences, the big index object will
// override the one from small index
func GetAddOnDeploymentConfigValues(
getter AddOnDeploymentConfigGetter, toValuesFuncs ...AddOnDeploymentConfigToValuesFunc) GetValuesFunc {
getter utils.AddOnDeploymentConfigGetter, toValuesFuncs ...AddOnDeploymentConfigToValuesFunc) GetValuesFunc {
return func(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn) (Values, error) {
var lastValues = Values{}
for _, config := range addon.Status.ConfigReferences {
Expand Down Expand Up @@ -360,7 +340,7 @@ func getRegistriesFromClusterAnnotation(
// - the imageKey can support the nested key, for example: "global.imageOverrides.helloWorldImage", the output
// will be: {"global": {"imageOverrides": {"helloWorldImage": "quay.io/ocm/addon-agent:v1"}}}
// - Image registries configured in the addonDeploymentConfig will take precedence over the managed cluster annotation
func GetAgentImageValues(getter AddOnDeploymentConfigGetter, imageKey, image string) GetValuesFunc {
func GetAgentImageValues(getter utils.AddOnDeploymentConfigGetter, imageKey, image string) GetValuesFunc {
return func(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn) (Values, error) {

// Get image from AddOnDeploymentConfig
Expand Down
120 changes: 120 additions & 0 deletions pkg/utils/addon_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package utils

import (
"context"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"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"
addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned"
)

// AddOnDeploymentConfigGetter has a method to return a AddOnDeploymentConfig object
type AddOnDeploymentConfigGetter interface {
Get(ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error)
}

type defaultAddOnDeploymentConfigGetter struct {
addonClient addonv1alpha1client.Interface
}

// NewAddOnDeploymentConfigGetter returns a AddOnDeploymentConfigGetter with addon client
func NewAddOnDeploymentConfigGetter(addonClient addonv1alpha1client.Interface) AddOnDeploymentConfigGetter {
return &defaultAddOnDeploymentConfigGetter{addonClient: addonClient}
}

func (g *defaultAddOnDeploymentConfigGetter) Get(
ctx context.Context, namespace, name string) (*addonapiv1alpha1.AddOnDeploymentConfig, error) {
return g.addonClient.AddonV1alpha1().AddOnDeploymentConfigs(namespace).Get(ctx, name, metav1.GetOptions{})
}

// AgentInstallNamespaceFromDeploymentConfigFunc returns an agent install namespace helper function which will get the
// namespace from the addon deployment config. If the addon does not support addon deployment config or there is no
// matched addon deployment config, it will return an empty string.
func AgentInstallNamespaceFromDeploymentConfigFunc(
adcgetter AddOnDeploymentConfigGetter,
) func(*addonapiv1alpha1.ManagedClusterAddOn) string {
return func(addon *addonapiv1alpha1.ManagedClusterAddOn) string {
if addon == nil {
utilruntime.HandleError(fmt.Errorf("failed to get addon install namespace, addon is nil"))
return ""
}

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

return config.Spec.AgentInstallNamespace
}
}

// GetDesiredAddOnDeployment returns the desired addonDeploymentConfig of the addon
func GetDesiredAddOnDeploymentConfig(
addon *addonapiv1alpha1.ManagedClusterAddOn,
adcgetter AddOnDeploymentConfigGetter,
) (*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.Get(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,
})
}

// 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{}
}
Loading

0 comments on commit 171db9a

Please sign in to comment.