Support setting manifest work config by addon template (#308)

*  Support setting manifest work config by addon template

Signed-off-by: zhujian <jiazhu@redhat.com>

* add unit tests

Signed-off-by: zhujian <jiazhu@redhat.com>

---------

Signed-off-by: zhujian <jiazhu@redhat.com>
This commit is contained in:
Jian Zhu
2023-11-20 14:09:38 +00:00
committed by GitHub
parent 615f5a4441
commit 6571a8a4d9
18 changed files with 472 additions and 52 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ require (
k8s.io/klog/v2 v2.100.1
k8s.io/kube-aggregator v0.28.1
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
open-cluster-management.io/addon-framework v0.8.1-0.20231009020812-e52774032b4c
open-cluster-management.io/addon-framework v0.8.1-0.20231102082339-51742bc299f2
open-cluster-management.io/api v0.12.1-0.20231114013807-95119cf22df6
sigs.k8s.io/controller-runtime v0.15.0
sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96
+2 -2
View File
@@ -738,8 +738,8 @@ k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5Ohx
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
open-cluster-management.io/addon-framework v0.8.1-0.20231009020812-e52774032b4c h1:9Rvj3UTjVwJWOItlIYx6shFF72f8L3t91T9IwZ8sx6Q=
open-cluster-management.io/addon-framework v0.8.1-0.20231009020812-e52774032b4c/go.mod h1:r4sQGR9YgLC4hXC695sfinun2WhuigWrEPk2IeIl800=
open-cluster-management.io/addon-framework v0.8.1-0.20231102082339-51742bc299f2 h1:38vY9paEGugvXfYGJ0oFabL4/8Jxrg+GnxxjUO2DMio=
open-cluster-management.io/addon-framework v0.8.1-0.20231102082339-51742bc299f2/go.mod h1:aj97pgpGJ0/LpQzBVtU2oDFqqIiZLOPnsjLKG/sVkFw=
open-cluster-management.io/api v0.12.1-0.20231114013807-95119cf22df6 h1:QwQ3dTtbCGuZ7iGqINuawGh4c3NmGe2qhcdHvEXRsOs=
open-cluster-management.io/api v0.12.1-0.20231114013807-95119cf22df6/go.mod h1:/I/nFccB0tmF+dZg7pHuzY3SaXOX86MI4vcFtidJ0OM=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+22 -4
View File
@@ -52,16 +52,34 @@ func AddonManagerNamespace() string {
func (a *CRDTemplateAgentAddon) GetDesiredAddOnTemplate(addon *addonapiv1alpha1.ManagedClusterAddOn,
clusterName, addonName string) (*addonapiv1alpha1.AddOnTemplate, error) {
if addon != nil {
return a.getDesiredAddOnTemplateInner(addon.Name, addon.Status.ConfigReferences)
}
if addon == nil {
var err error
addon, err = a.addonLister.ManagedClusterAddOns(clusterName).Get(addonName)
if len(clusterName) != 0 {
addon, err := a.addonLister.ManagedClusterAddOns(clusterName).Get(addonName)
if err != nil {
return nil, err
}
return a.getDesiredAddOnTemplateInner(addon.Name, addon.Status.ConfigReferences)
}
return a.GetDesiredAddOnTemplateByAddon(addon)
// clusterName and addon are both empty, backoff to get the template from the clusterManagementAddOn
cma, err := a.cmaLister.Get(addonName)
if err != nil {
return nil, err
}
// convert the DefaultConfigReference to ConfigReference
var configReferences []addonapiv1alpha1.ConfigReference
for _, configReference := range cma.Status.DefaultConfigReferences {
configReferences = append(configReferences, addonapiv1alpha1.ConfigReference{
ConfigGroupResource: configReference.ConfigGroupResource,
DesiredConfig: configReference.DesiredConfig,
})
}
return a.getDesiredAddOnTemplateInner(cma.Name, configReferences)
}
func (a *CRDTemplateAgentAddon) TemplateCSRConfigurationsFunc() func(cluster *clusterv1.ManagedCluster) []addonapiv1alpha1.RegistrationConfig {
@@ -4,10 +4,12 @@ import (
"bytes"
"context"
"fmt"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
certificatesv1 "k8s.io/api/certificates/v1"
certificates "k8s.io/api/certificates/v1beta1"
rbacv1 "k8s.io/api/rbac/v1"
@@ -665,3 +667,45 @@ func TestTemplatePermissionConfigFunc(t *testing.T) {
}
}
}
func TestAddonManagerNamespace(t *testing.T) {
cases := []struct {
name string
podNamespace string
envNs string
expected string
}{
{
name: "pod namespace is not empty",
podNamespace: "test",
envNs: "",
expected: "test",
},
{
name: "pod namespace is empty, env is not empty",
podNamespace: "",
envNs: "test-env",
expected: "test-env",
},
{
name: "default namespace",
podNamespace: "",
envNs: "",
expected: "open-cluster-management-hub",
},
}
for _, c := range cases {
if c.podNamespace != "" {
podNamespace = c.podNamespace
}
if c.envNs != "" {
os.Setenv("POD_NAMESPACE", c.envNs)
}
ns := AddonManagerNamespace()
assert.Equal(t, c.expected, ns)
// reset podNamespace and env
podNamespace = ""
os.Setenv("POD_NAMESPACE", "")
}
}
+26 -9
View File
@@ -8,6 +8,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
rbacv1lister "k8s.io/client-go/listers/rbac/v1"
"k8s.io/klog/v2"
@@ -54,6 +55,7 @@ type CRDTemplateAgentAddon struct {
addonClient addonv1alpha1client.Interface
addonLister addonlisterv1alpha1.ManagedClusterAddOnLister
addonTemplateLister addonlisterv1alpha1.AddOnTemplateLister
cmaLister addonlisterv1alpha1.ClusterManagementAddOnLister
rolebindingLister rbacv1lister.RoleBindingLister
addonName string
agentName string
@@ -79,6 +81,7 @@ func NewCRDTemplateAgentAddon(
addonClient: addonClient,
addonLister: addonInformers.Addon().V1alpha1().ManagedClusterAddOns().Lister(),
addonTemplateLister: addonInformers.Addon().V1alpha1().AddOnTemplates().Lister(),
cmaLister: addonInformers.Addon().V1alpha1().ClusterManagementAddOns().Lister(),
rolebindingLister: rolebindingLister,
addonName: addonName,
agentName: agentName,
@@ -91,7 +94,7 @@ func (a *CRDTemplateAgentAddon) Manifests(
cluster *clusterv1.ManagedCluster,
addon *addonapiv1alpha1.ManagedClusterAddOn) ([]runtime.Object, error) {
template, err := a.GetDesiredAddOnTemplateByAddon(addon)
template, err := a.getDesiredAddOnTemplateInner(addon.Name, addon.Status.ConfigReferences)
if err != nil {
return nil, err
}
@@ -107,7 +110,7 @@ func (a *CRDTemplateAgentAddon) GetAgentAddonOptions() agent.AgentAddonOptions {
for gvr := range utils.BuiltInAddOnConfigGVRs {
supportedConfigGVRs = append(supportedConfigGVRs, gvr)
}
return agent.AgentAddonOptions{
agentAddonOptions := agent.AgentAddonOptions{
AddonName: a.addonName,
InstallStrategy: nil,
HealthProber: &agent.HealthProber{
@@ -124,6 +127,19 @@ func (a *CRDTemplateAgentAddon) GetAgentAddonOptions() agent.AgentAddonOptions {
},
AgentDeployTriggerClusterFilter: utils.ClusterImageRegistriesAnnotationChanged,
}
template, err := a.GetDesiredAddOnTemplate(nil, "", a.addonName)
if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to get addon %s template: %v", a.addonName, err))
return agentAddonOptions
}
if template == nil {
utilruntime.HandleError(fmt.Errorf("addon %s template is nil", a.addonName))
return agentAddonOptions
}
agentAddonOptions.ManifestConfigs = template.Spec.AgentSpec.ManifestConfigs
return agentAddonOptions
}
func (a *CRDTemplateAgentAddon) renderObjects(
@@ -190,19 +206,20 @@ func (a *CRDTemplateAgentAddon) decorateObjects(
return objects, nil
}
// GetDesiredAddOnTemplateByAddon returns the desired template of the addon
func (a *CRDTemplateAgentAddon) GetDesiredAddOnTemplateByAddon(
addon *addonapiv1alpha1.ManagedClusterAddOn) (*addonapiv1alpha1.AddOnTemplate, error) {
ok, templateRef := AddonTemplateConfigRef(addon.Status.ConfigReferences)
// getDesiredAddOnTemplateInner returns the desired template of the addon
func (a *CRDTemplateAgentAddon) getDesiredAddOnTemplateInner(
addonName string, configReferences []addonapiv1alpha1.ConfigReference,
) (*addonapiv1alpha1.AddOnTemplate, error) {
ok, templateRef := AddonTemplateConfigRef(configReferences)
if !ok {
a.logger.V(4).Info("Addon template config in status is empty", "addonName", addon.Name)
a.logger.V(4).Info("Addon template config in status is empty", "addonName", addonName)
return nil, nil
}
desiredTemplate := templateRef.DesiredConfig
if desiredTemplate == nil || desiredTemplate.SpecHash == "" {
a.logger.Info("Addon template spec hash is empty", "addonName", addon.Name)
return nil, fmt.Errorf("addon %s template desired spec hash is empty", addon.Name)
a.logger.Info("Addon template spec hash is empty", "addonName", addonName)
return nil, fmt.Errorf("addon %s template desired spec hash is empty", addonName)
}
template, err := a.addonTemplateLister.Get(desiredTemplate.Name)
@@ -19,12 +19,14 @@ import (
"k8s.io/klog/v2/ktesting"
"open-cluster-management.io/addon-framework/pkg/addonfactory"
"open-cluster-management.io/addon-framework/pkg/addonmanager/addontesting"
"open-cluster-management.io/addon-framework/pkg/utils"
addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
fakeaddon "open-cluster-management.io/api/client/addon/clientset/versioned/fake"
addoninformers "open-cluster-management.io/api/client/addon/informers/externalversions"
clusterv1 "open-cluster-management.io/api/cluster/v1"
clusterv1apha1 "open-cluster-management.io/api/cluster/v1alpha1"
workapiv1 "open-cluster-management.io/api/work/v1"
)
func TestAddonTemplateAgentManifests(t *testing.T) {
@@ -385,6 +387,170 @@ func TestAgentInstallNamespace(t *testing.T) {
}
}
func TestAgentManifestConfigs(t *testing.T) {
_, ctx := ktesting.NewTestContext(t)
addonName := "hello"
s := runtime.NewScheme()
_ = scheme.AddToScheme(s)
_ = clusterv1apha1.Install(s)
_ = addonapiv1alpha1.Install(s)
cases := []struct {
name string
addonTemplate *addonapiv1alpha1.AddOnTemplate
clusterManagementAddOn *addonapiv1alpha1.ClusterManagementAddOn
expected []workapiv1.ManifestConfigOption
}{
{
name: "no addontemplate in cma status",
clusterManagementAddOn: addontesting.NewClusterManagementAddon("hello", "", "").Build(),
expected: nil,
},
{
name: "no addontemplate",
clusterManagementAddOn: addontesting.NewClusterManagementAddon("hello", "", "").
WithDefaultConfigReferences(
addonapiv1alpha1.DefaultConfigReference{
ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{
Group: utils.AddOnTemplateGVR.Group,
Resource: utils.AddOnTemplateGVR.Resource,
},
DesiredConfig: &addonapiv1alpha1.ConfigSpecHash{
ConfigReferent: addonapiv1alpha1.ConfigReferent{Name: "hello-template"},
SpecHash: "hash",
},
},
).Build(),
expected: nil,
},
{
name: "addontemplate does not have manifestconfigs",
clusterManagementAddOn: addontesting.NewClusterManagementAddon("hello", "", "").
WithDefaultConfigReferences(
addonapiv1alpha1.DefaultConfigReference{
ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{
Group: utils.AddOnTemplateGVR.Group,
Resource: utils.AddOnTemplateGVR.Resource,
},
DesiredConfig: &addonapiv1alpha1.ConfigSpecHash{
ConfigReferent: addonapiv1alpha1.ConfigReferent{Name: "hello-template"},
SpecHash: "hash",
},
},
).Build(),
addonTemplate: &addonapiv1alpha1.AddOnTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "hello-template",
},
},
expected: nil,
},
{
name: "addontemplate has manifestconfigs",
clusterManagementAddOn: addontesting.NewClusterManagementAddon("hello", "", "").
WithDefaultConfigReferences(
addonapiv1alpha1.DefaultConfigReference{
ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{
Group: utils.AddOnTemplateGVR.Group,
Resource: utils.AddOnTemplateGVR.Resource,
},
DesiredConfig: &addonapiv1alpha1.ConfigSpecHash{
ConfigReferent: addonapiv1alpha1.ConfigReferent{Name: "hello-template"},
SpecHash: "hash",
},
},
).Build(),
addonTemplate: &addonapiv1alpha1.AddOnTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "hello-template",
},
Spec: addonapiv1alpha1.AddOnTemplateSpec{
AddonName: "hello",
AgentSpec: workapiv1.ManifestWorkSpec{
ManifestConfigs: []workapiv1.ManifestConfigOption{
{
ResourceIdentifier: workapiv1.ResourceIdentifier{
Group: "apps",
Resource: "deployment",
Name: "hello",
Namespace: "default",
},
FeedbackRules: []workapiv1.FeedbackRule{
{
Type: workapiv1.WellKnownStatusType,
},
},
},
},
},
},
},
expected: []workapiv1.ManifestConfigOption{
{
ResourceIdentifier: workapiv1.ResourceIdentifier{
Group: "apps",
Resource: "deployment",
Name: "hello",
Namespace: "default",
},
FeedbackRules: []workapiv1.FeedbackRule{
{
Type: workapiv1.WellKnownStatusType,
},
},
},
},
},
}
for _, tc := range cases {
var objs []runtime.Object
if tc.clusterManagementAddOn != nil {
objs = append(objs, tc.clusterManagementAddOn)
}
if tc.addonTemplate != nil {
objs = append(objs, tc.addonTemplate)
}
hubKubeClient := fakekube.NewSimpleClientset()
addonClient := fakeaddon.NewSimpleClientset(objs...)
addonInformerFactory := addoninformers.NewSharedInformerFactory(addonClient, 30*time.Minute)
if tc.clusterManagementAddOn != nil {
cmaStore := addonInformerFactory.Addon().V1alpha1().ClusterManagementAddOns().Informer().GetStore()
if err := cmaStore.Add(tc.clusterManagementAddOn); err != nil {
t.Fatal(err)
}
}
if tc.addonTemplate != nil {
atStore := addonInformerFactory.Addon().V1alpha1().AddOnTemplates().Informer().GetStore()
if err := atStore.Add(tc.addonTemplate); err != nil {
t.Fatal(err)
}
}
kubeInformers := kubeinformers.NewSharedInformerFactoryWithOptions(hubKubeClient, 10*time.Minute)
agentAddon := NewCRDTemplateAgentAddon(
ctx,
addonName,
"test-agent",
hubKubeClient,
addonClient,
addonInformerFactory,
kubeInformers.Rbac().V1().RoleBindings().Lister(),
addonfactory.GetAddOnDeploymentConfigValues(
addonfactory.NewAddOnDeploymentConfigGetter(addonClient),
addonfactory.ToAddOnCustomizedVariableValues,
ToAddOnNodePlacementPrivateValues,
ToAddOnRegistriesPrivateValues,
),
)
agentManifestConfigs := agentAddon.GetAgentAddonOptions().ManifestConfigs
assert.Equal(t, tc.expected, agentManifestConfigs)
}
}
type testManagedClusterAddOnBuilder struct {
managedClusterAddOn *addonapiv1alpha1.ManagedClusterAddOn
addonTemplate *addonapiv1alpha1.AddOnTemplate
+24
View File
@@ -19,6 +19,7 @@ import (
addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
clusterv1 "open-cluster-management.io/api/cluster/v1"
workapiv1 "open-cluster-management.io/api/work/v1"
"open-cluster-management.io/ocm/pkg/addon/templateagent"
"open-cluster-management.io/ocm/test/e2e/manifests"
@@ -209,6 +210,29 @@ var _ = ginkgo.Describe("Enable addon management feature gate", ginkgo.Ordered,
return nil
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
ginkgo.By("Make sure manifestwork config is configured")
manifestWork, err := t.HubWorkClient.WorkV1().ManifestWorks(clusterName).Get(context.Background(),
fmt.Sprintf("addon-%s-deploy-0", addOnName), metav1.GetOptions{})
gomega.Expect(err).ToNot(gomega.HaveOccurred())
foundConfig := false
expectedResourceIdentifier := workapiv1.ResourceIdentifier{
Group: "apps",
Resource: "deployments",
Name: "hello-template-agent",
Namespace: addonInstallNamespace,
}
for _, mc := range manifestWork.Spec.ManifestConfigs {
if mc.ResourceIdentifier == expectedResourceIdentifier {
foundConfig = true
gomega.Expect(mc.UpdateStrategy.Type).To(gomega.Equal(workapiv1.UpdateStrategyTypeServerSideApply))
break
}
}
if !foundConfig {
gomega.Expect(fmt.Errorf("expected manifestwork is not correct, %v",
manifestWork.Spec.ManifestConfigs)).ToNot(gomega.HaveOccurred())
}
ginkgo.By(fmt.Sprintf("delete the addon %v on the managed cluster namespace %v", addOnName, clusterName))
err = t.AddOnClinet.AddonV1alpha1().ManagedClusterAddOns(clusterName).Delete(
context.TODO(), addOnName, metav1.DeleteOptions{})
@@ -5,6 +5,14 @@ metadata:
spec:
addonName: hello-template
agentSpec:
manifestConfigs:
- resourceIdentifier:
group: apps
name: hello-template-agent
namespace: << AddonInstallNamespace >>
resource: deployments
updateStrategy:
type: ServerSideApply
workload:
manifests:
- kind: Deployment
+1 -1
View File
@@ -1423,7 +1423,7 @@ k8s.io/utils/pointer
k8s.io/utils/ptr
k8s.io/utils/strings/slices
k8s.io/utils/trace
# open-cluster-management.io/addon-framework v0.8.1-0.20231009020812-e52774032b4c
# open-cluster-management.io/addon-framework v0.8.1-0.20231102082339-51742bc299f2
## explicit; go 1.20
open-cluster-management.io/addon-framework/pkg/addonfactory
open-cluster-management.io/addon-framework/pkg/addonmanager
@@ -154,6 +154,10 @@ func ToAddOnProxyConfigValues(config addonapiv1alpha1.AddOnDeploymentConfig) (Va
proxyConfig["NO_PROXY"] = config.Spec.ProxyConfig.NoProxy
}
if len(config.Spec.ProxyConfig.CABundle) > 0 {
proxyConfig["PROXY_CA_BUNDLE"] = string(config.Spec.ProxyConfig.CABundle)
}
type global struct {
ProxyConfig map[string]string `json:"proxyConfig"`
}
@@ -264,6 +268,9 @@ func ToAddOnDeploymentConfigValues(config addonapiv1alpha1.AddOnDeploymentConfig
if len(config.Spec.ProxyConfig.NoProxy) > 0 {
values["NoProxy"] = config.Spec.ProxyConfig.NoProxy
}
if len(config.Spec.ProxyConfig.CABundle) > 0 {
values["ProxyCABundle"] = string(config.Spec.ProxyConfig.CABundle)
}
return values, nil
}
@@ -34,8 +34,9 @@ type AgentAddonFactory struct {
getValuesFuncs []GetValuesFunc
agentAddonOptions agent.AgentAddonOptions
// trimCRDDescription flag is used to trim the description of CRDs in manifestWork. disabled by default.
trimCRDDescription bool
hostingCluster *clusterv1.ManagedCluster
trimCRDDescription bool
hostingCluster *clusterv1.ManagedCluster
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
}
// NewAgentAddonFactory builds an addonAgentFactory instance with addon name and fs.
@@ -78,6 +79,8 @@ func (f *AgentAddonFactory) WithGetValuesFuncs(getValuesFuncs ...GetValuesFunc)
}
// WithInstallStrategy defines the installation strategy of the manifests prescribed by Manifests(..).
// Deprecated: add annotation "addon.open-cluster-management.io/lifecycle: addon-manager" to ClusterManagementAddon
// and define install strategy in ClusterManagementAddon spec.installStrategy instead.
func (f *AgentAddonFactory) WithInstallStrategy(strategy *agent.InstallStrategy) *AgentAddonFactory {
if strategy.InstallNamespace == "" {
strategy.InstallNamespace = AddonDefaultInstallNamespace
@@ -142,8 +145,29 @@ func (f *AgentAddonFactory) WithAgentDeployTriggerClusterFilter(
return f
}
// WithAgentInstallNamespace defines the namespace where the agent resources will be deployed, this will
// override the default built-in namespace value; And if the registrationOption is not nil but the
// registrationOption.AgentInstallNamespace is nil, this will also set it to this.
func (f *AgentAddonFactory) WithAgentInstallNamespace(
nsFunc func(addon *addonapiv1alpha1.ManagedClusterAddOn) string,
) *AgentAddonFactory {
f.agentInstallNamespace = nsFunc
return f
}
// preBuildAddon sets the default values for the agentAddonOptions.
func (f *AgentAddonFactory) preBuildAddon() {
if f.agentInstallNamespace != nil {
if f.agentAddonOptions.Registration != nil && f.agentAddonOptions.Registration.AgentInstallNamespace == nil {
f.agentAddonOptions.Registration.AgentInstallNamespace = f.agentInstallNamespace
}
}
}
// BuildHelmAgentAddon builds a helm agentAddon instance.
func (f *AgentAddonFactory) BuildHelmAgentAddon() (agent.AgentAddon, error) {
f.preBuildAddon()
if err := validateSupportedConfigGVRs(f.agentAddonOptions.SupportedConfigGVRs); err != nil {
return nil, err
}
@@ -160,6 +184,8 @@ func (f *AgentAddonFactory) BuildHelmAgentAddon() (agent.AgentAddon, error) {
// BuildTemplateAgentAddon builds a template agentAddon instance.
func (f *AgentAddonFactory) BuildTemplateAgentAddon() (agent.AgentAddon, error) {
f.preBuildAddon()
if err := validateSupportedConfigGVRs(f.agentAddonOptions.SupportedConfigGVRs); err != nil {
return nil, err
}
@@ -42,22 +42,24 @@ type helmDefaultValues struct {
}
type HelmAgentAddon struct {
decoder runtime.Decoder
chart *chart.Chart
getValuesFuncs []GetValuesFunc
agentAddonOptions agent.AgentAddonOptions
trimCRDDescription bool
hostingCluster *clusterv1.ManagedCluster
decoder runtime.Decoder
chart *chart.Chart
getValuesFuncs []GetValuesFunc
agentAddonOptions agent.AgentAddonOptions
trimCRDDescription bool
hostingCluster *clusterv1.ManagedCluster
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
}
func newHelmAgentAddon(factory *AgentAddonFactory, chart *chart.Chart) *HelmAgentAddon {
return &HelmAgentAddon{
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
chart: chart,
getValuesFuncs: factory.getValuesFuncs,
agentAddonOptions: factory.agentAddonOptions,
trimCRDDescription: factory.trimCRDDescription,
hostingCluster: factory.hostingCluster,
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
chart: chart,
getValuesFuncs: factory.getValuesFuncs,
agentAddonOptions: factory.agentAddonOptions,
trimCRDDescription: factory.trimCRDDescription,
hostingCluster: factory.hostingCluster,
agentInstallNamespace: factory.agentInstallNamespace,
}
}
@@ -176,7 +178,7 @@ func (a *HelmAgentAddon) getValues(
overrideValues = MergeValues(overrideValues, builtinValues)
values, err := chartutil.ToRenderValues(a.chart, overrideValues,
a.releaseOptions(cluster, addon), a.capabilities(cluster, addon))
a.releaseOptions(addon), a.capabilities(cluster, addon))
if err != nil {
klog.Errorf("failed to render helm chart with values %v. err:%v", overrideValues, err)
return values, err
@@ -185,17 +187,27 @@ func (a *HelmAgentAddon) getValues(
return values, nil
}
func (a *HelmAgentAddon) getValueAgentInstallNamespace(addon *addonapiv1alpha1.ManagedClusterAddOn) string {
installNamespace := addon.Spec.InstallNamespace
if len(installNamespace) == 0 {
installNamespace = AddonDefaultInstallNamespace
}
if a.agentInstallNamespace != nil {
ns := a.agentInstallNamespace(addon)
if len(ns) > 0 {
installNamespace = ns
}
}
return installNamespace
}
func (a *HelmAgentAddon) getBuiltinValues(
cluster *clusterv1.ManagedCluster,
addon *addonapiv1alpha1.ManagedClusterAddOn) (Values, error) {
builtinValues := helmBuiltinValues{}
builtinValues.ClusterName = cluster.GetName()
installNamespace := addon.Spec.InstallNamespace
if len(installNamespace) == 0 {
installNamespace = AddonDefaultInstallNamespace
}
builtinValues.AddonInstallNamespace = installNamespace
builtinValues.AddonInstallNamespace = a.getValueAgentInstallNamespace(addon)
builtinValues.InstallMode, _ = constants.GetHostedModeInfo(addon.GetAnnotations())
@@ -242,11 +254,9 @@ func (a *HelmAgentAddon) capabilities(
// only support Release.Name, Release.Namespace
func (a *HelmAgentAddon) releaseOptions(
cluster *clusterv1.ManagedCluster,
addon *addonapiv1alpha1.ManagedClusterAddOn) chartutil.ReleaseOptions {
installNamespace := addon.Spec.InstallNamespace
if len(installNamespace) == 0 {
installNamespace = AddonDefaultInstallNamespace
return chartutil.ReleaseOptions{
Name: a.agentAddonOptions.AddonName,
Namespace: a.getValueAgentInstallNamespace(addon),
}
return chartutil.ReleaseOptions{Name: a.agentAddonOptions.AddonName, Namespace: installNamespace}
}
@@ -37,19 +37,21 @@ type templateFile struct {
}
type TemplateAgentAddon struct {
decoder runtime.Decoder
templateFiles []templateFile
getValuesFuncs []GetValuesFunc
agentAddonOptions agent.AgentAddonOptions
trimCRDDescription bool
decoder runtime.Decoder
templateFiles []templateFile
getValuesFuncs []GetValuesFunc
agentAddonOptions agent.AgentAddonOptions
trimCRDDescription bool
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
}
func newTemplateAgentAddon(factory *AgentAddonFactory) *TemplateAgentAddon {
return &TemplateAgentAddon{
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
getValuesFuncs: factory.getValuesFuncs,
agentAddonOptions: factory.agentAddonOptions,
trimCRDDescription: factory.trimCRDDescription,
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
getValuesFuncs: factory.getValuesFuncs,
agentAddonOptions: factory.agentAddonOptions,
trimCRDDescription: factory.trimCRDDescription,
agentInstallNamespace: factory.agentInstallNamespace,
}
}
@@ -123,6 +125,12 @@ func (a *TemplateAgentAddon) getBuiltinValues(
if len(installNamespace) == 0 {
installNamespace = AddonDefaultInstallNamespace
}
if a.agentInstallNamespace != nil {
ns := a.agentInstallNamespace(addon)
if len(ns) > 0 {
installNamespace = ns
}
}
builtinValues.AddonInstallNamespace = installNamespace
builtinValues.InstallMode, _ = constants.GetHostedModeInfo(addon.GetAnnotations())
@@ -221,6 +221,10 @@ func (s *healthCheckSyncer) analyzeDeploymentWorkProber(
deployments := utils.FilterDeployments(manifests)
for _, deployment := range deployments {
manifestConfig := utils.DeploymentWellKnowManifestConfig(deployment.Namespace, deployment.Name)
// only probe the deployment with non-zero replicas
if deployment.Spec.Replicas != nil && *deployment.Spec.Replicas == 0 {
continue
}
probeFields = append(probeFields, agent.ProbeField{
ResourceIdentifier: manifestConfig.ResourceIdentifier,
ProbeRules: manifestConfig.FeedbackRules,
@@ -517,9 +517,86 @@ func getManifestConfigOption(agentAddon agent.AgentAddon,
}
}
userConfiguredManifestConfigs := agentAddon.GetAgentAddonOptions().ManifestConfigs
for _, mc := range userConfiguredManifestConfigs {
index := containsResourceIdentifier(manifestConfigs, mc.ResourceIdentifier)
if index == -1 {
manifestConfigs = append(manifestConfigs, mc)
continue
}
// merge the feedback rules generated by the addon manager and the feedback rules configured by users
for _, rule := range mc.FeedbackRules {
manifestConfigs[index].FeedbackRules = mergeFeedbackRule(manifestConfigs[index].FeedbackRules, rule)
}
if mc.UpdateStrategy != nil {
manifestConfigs[index].UpdateStrategy = mc.UpdateStrategy
}
}
return manifestConfigs
}
func containsResourceIdentifier(mcs []workapiv1.ManifestConfigOption, ri workapiv1.ResourceIdentifier) int {
for index, mc := range mcs {
if mc.ResourceIdentifier == ri {
return index
}
}
return -1
}
// mergeFeedbackRule merges the new rule into the existing rules.
// If the new rule is json path type, will ignore the json path which
// is already in the existing rules, compare by the path name.
func mergeFeedbackRule(rules []workapiv1.FeedbackRule, rule workapiv1.FeedbackRule) []workapiv1.FeedbackRule {
rrules := rules
var existJsonPaths []workapiv1.JsonPath
var existWellKnownStatus bool = false
for _, rule := range rules {
if rule.Type == workapiv1.WellKnownStatusType {
existWellKnownStatus = true
continue
}
if rule.Type == workapiv1.JSONPathsType {
existJsonPaths = append(existJsonPaths, rule.JsonPaths...)
}
}
if rule.Type == workapiv1.WellKnownStatusType {
if !existWellKnownStatus {
rrules = append(rrules, rule)
}
return rrules
}
if rule.Type == workapiv1.JSONPathsType {
var jsonPaths []workapiv1.JsonPath
for _, path := range rule.JsonPaths {
found := false
for _, rpath := range existJsonPaths {
if path.Name == rpath.Name {
found = true
break
}
}
if !found {
jsonPaths = append(jsonPaths, path)
}
}
if len(jsonPaths) != 0 {
rrules = append(rrules, workapiv1.FeedbackRule{
Type: workapiv1.JSONPathsType,
JsonPaths: jsonPaths,
})
}
return rrules
}
rrules = append(rrules, rule)
return rrules
}
func getDeletionOrphaningRule(obj runtime.Object) (*workapiv1.OrphaningRule, error) {
accessor, err := meta.Accessor(obj)
if err != nil {
@@ -99,6 +99,7 @@ func (c *addonRegistrationController) sync(ctx context.Context, syncCtx factory.
// wait until the mca's ownerref is set.
if !utils.IsOwnedByCMA(managedClusterAddonCopy) {
klog.Warningf("OwnerReferences is not set for %q", key)
return nil
}
@@ -88,7 +88,16 @@ type AgentAddonOptions struct {
// AgentDeployTriggerClusterFilter: func(old, new *clusterv1.ManagedCluster) bool {
// return !equality.Semantic.DeepEqual(old.Annotations, new.Annotations)
// }
// +optional
AgentDeployTriggerClusterFilter func(old, new *clusterv1.ManagedCluster) bool
// ManifestConfigs represents the configurations of manifests defined in workload field. It will:
// - override the update strategy set by the "Updaters" field
// - merge the feedback rules set by the "HealthProber" field if they have the same resource identifier,
// when merging the feedback rules, if the rule configured here is json path type, will ignore the
// json path which is already in the existing rules, compare by the path name.
// +optional
ManifestConfigs []workapiv1.ManifestConfigOption
}
type CSRSignerFunc func(csr *certificatesv1.CertificateSigningRequest) []byte
@@ -76,7 +76,8 @@ func DeploymentAvailabilityHealthCheck(identifier workapiv1.ResourceIdentifier,
return nil
}
return fmt.Errorf("readyReplica is %d for deployment %s/%s", *value.Value.Integer, identifier.Namespace, identifier.Name)
return fmt.Errorf("readyReplica is %d for deployment %s/%s",
*value.Value.Integer, identifier.Namespace, identifier.Name)
}
return fmt.Errorf("readyReplica is not probed")
}