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

✨ Set install namespace of addonTemplate from config #417

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
4 changes: 3 additions & 1 deletion pkg/addon/controllers/addontemplate/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"open-cluster-management.io/addon-framework/pkg/addonfactory"
"open-cluster-management.io/addon-framework/pkg/addonmanager"
"open-cluster-management.io/addon-framework/pkg/utils"
addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned"
Expand Down Expand Up @@ -194,10 +195,11 @@ func (c *addonTemplateController) runController(
// image overrides from cluster annotation has lower priority than from the addonDeploymentConfig
getValuesClosure,
addonfactory.GetAddOnDeploymentConfigValues(
addonfactory.NewAddOnDeploymentConfigGetter(c.addonClient),
utils.NewAddOnDeploymentConfigGetter(c.addonClient),
addonfactory.ToAddOnCustomizedVariableValues,
templateagent.ToAddOnNodePlacementPrivateValues,
templateagent.ToAddOnRegistriesPrivateValues,
templateagent.ToAddOnInstallNamespacePrivateValues,
),
)
err = mgr.AddAgent(agentAddon)
Expand Down
164 changes: 141 additions & 23 deletions pkg/addon/templateagent/decorator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,146 @@ import (
"fmt"
"strings"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"

"open-cluster-management.io/addon-framework/pkg/addonfactory"
"open-cluster-management.io/addon-framework/pkg/utils"
addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
)

type deploymentDecorator interface {
// decorator mutate the unstructured and returns an unstructured.
type decorator interface {
decorate(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
}

type namespaceDecorator struct {
installNamespace string
// paths is the paths of a resource kind that the decorator needs to set namespace field in it.
// if the returned object is a list, decorator will set namespace for each item.
paths map[string]string
}

func newNamespaceDecorator(privateValues addonfactory.Values) *namespaceDecorator {
decorator := &namespaceDecorator{
paths: map[string]string{
"ClusterRoleBinding": "subjects",
"RoleBinding": "subjects",
},
}
namespace, ok := privateValues[InstallNamespacePrivateValueKey]
if ok {
decorator.installNamespace = namespace.(string)
}

return decorator
}

func (d *namespaceDecorator) decorate(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
if len(d.installNamespace) == 0 {
return obj, nil
}

// set namespace for all manifests, if the manifest is cluster scoped, namespace will be ignored when
// being applied.
obj.SetNamespace(d.installNamespace)

path, ok := d.paths[obj.GetKind()]
if !ok {
return obj, nil
}

field, found, err := unstructured.NestedFieldNoCopy(obj.Object, path)
if err != nil {
return obj, err
}
if !found {
return obj, fmt.Errorf("failed to find the path %s for kind %s", path, obj.GetKind())
}

// it cannot supported nested structure, only list or map.
switch f := field.(type) {
case []interface{}:
for _, item := range f {
if err := setNamespaceForObject(item, d.installNamespace); err != nil {
return obj, err
}
}
case interface{}:
if err := setNamespaceForObject(f, d.installNamespace); err != nil {
return obj, err
}
}
return obj, nil
}

func setNamespaceForObject(obj interface{}, namespace string) error {
mapVal, ok := obj.(map[string]interface{})
if !ok {
return fmt.Errorf("obj %v is not a map, cannot set", obj)
}
mapVal["namespace"] = namespace
return nil
}

type deploymentDecorator struct {
decorators []podTemplateSpecDecorator
}

func newDeploymentDecorator(
addonName string,
template *addonapiv1alpha1.AddOnTemplate,
orderedValues orderedValues,
privateValues addonfactory.Values,
) decorator {
return &deploymentDecorator{
decorators: []podTemplateSpecDecorator{
newEnvironmentDecorator(orderedValues),
newVolumeDecorator(addonName, template),
newNodePlacementDecorator(privateValues),
newImageDecorator(privateValues),
},
}
}

func (d *deploymentDecorator) decorate(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
deployment, err := utils.ConvertToDeployment(obj)
// not a deployment, directly return
if err != nil {
return obj, nil
}

for _, decorator := range d.decorators {
err = decorator.decorate(&deployment.Spec.Template)
if err != nil {
return obj, err
}
}

result, err := runtime.DefaultUnstructuredConverter.ToUnstructured(deployment)
if err != nil {
return obj, err
}

return &unstructured.Unstructured{Object: result}, nil
}

type podTemplateSpecDecorator interface {
// decorate modifies the deployment in place
decorate(deployment *appsv1.Deployment) error
decorate(pod *corev1.PodTemplateSpec) error
}

type environmentDecorator struct {
orderedValues orderedValues
}

func newEnvironmentDecorator(orderedValues orderedValues) deploymentDecorator {
func newEnvironmentDecorator(orderedValues orderedValues) podTemplateSpecDecorator {
return &environmentDecorator{
orderedValues: orderedValues,
}
}
func (d *environmentDecorator) decorate(deployment *appsv1.Deployment) error {
func (d *environmentDecorator) decorate(pod *corev1.PodTemplateSpec) error {
envVars := make([]corev1.EnvVar, len(d.orderedValues))
for index, value := range d.orderedValues {
envVars[index] = corev1.EnvVar{
Expand All @@ -34,9 +152,9 @@ func (d *environmentDecorator) decorate(deployment *appsv1.Deployment) error {
}
}

for j := range deployment.Spec.Template.Spec.Containers {
deployment.Spec.Template.Spec.Containers[j].Env = append(
deployment.Spec.Template.Spec.Containers[j].Env,
for j := range pod.Spec.Containers {
pod.Spec.Containers[j].Env = append(
pod.Spec.Containers[j].Env,
envVars...)
}

Expand All @@ -48,14 +166,14 @@ type volumeDecorator struct {
addonName string
}

func newVolumeDecorator(addonName string, template *addonapiv1alpha1.AddOnTemplate) deploymentDecorator {
func newVolumeDecorator(addonName string, template *addonapiv1alpha1.AddOnTemplate) podTemplateSpecDecorator {
return &volumeDecorator{
addonName: addonName,
template: template,
}
}

func (d *volumeDecorator) decorate(deployment *appsv1.Deployment) error {
func (d *volumeDecorator) decorate(pod *corev1.PodTemplateSpec) error {

volumeMounts := []corev1.VolumeMount{}
volumes := []corev1.Volume{}
Expand Down Expand Up @@ -102,12 +220,12 @@ func (d *volumeDecorator) decorate(deployment *appsv1.Deployment) error {
return nil
}

for j := range deployment.Spec.Template.Spec.Containers {
deployment.Spec.Template.Spec.Containers[j].VolumeMounts = append(
deployment.Spec.Template.Spec.Containers[j].VolumeMounts, volumeMounts...)
for j := range pod.Spec.Containers {
pod.Spec.Containers[j].VolumeMounts = append(
pod.Spec.Containers[j].VolumeMounts, volumeMounts...)
}

deployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, volumes...)
pod.Spec.Volumes = append(pod.Spec.Volumes, volumes...)

return nil
}
Expand All @@ -116,13 +234,13 @@ type nodePlacementDecorator struct {
privateValues addonfactory.Values
}

func newNodePlacementDecorator(privateValues addonfactory.Values) deploymentDecorator {
func newNodePlacementDecorator(privateValues addonfactory.Values) podTemplateSpecDecorator {
return &nodePlacementDecorator{
privateValues: privateValues,
}
}

func (d *nodePlacementDecorator) decorate(deployment *appsv1.Deployment) error {
func (d *nodePlacementDecorator) decorate(pod *corev1.PodTemplateSpec) error {
nodePlacement, ok := d.privateValues[NodePlacementPrivateValueKey]
if !ok {
return nil
Expand All @@ -134,11 +252,11 @@ func (d *nodePlacementDecorator) decorate(deployment *appsv1.Deployment) error {
}

if np.NodeSelector != nil {
deployment.Spec.Template.Spec.NodeSelector = np.NodeSelector
pod.Spec.NodeSelector = np.NodeSelector
}

if np.NodeSelector != nil {
deployment.Spec.Template.Spec.Tolerations = np.Tolerations
pod.Spec.Tolerations = np.Tolerations
}

return nil
Expand All @@ -148,13 +266,13 @@ type imageDecorator struct {
privateValues addonfactory.Values
}

func newImageDecorator(privateValues addonfactory.Values) deploymentDecorator {
func newImageDecorator(privateValues addonfactory.Values) podTemplateSpecDecorator {
return &imageDecorator{
privateValues: privateValues,
}
}

func (d *imageDecorator) decorate(deployment *appsv1.Deployment) error {
func (d *imageDecorator) decorate(pod *corev1.PodTemplateSpec) error {
registries, ok := d.privateValues[RegistriesPrivateValueKey]
if !ok {
return nil
Expand All @@ -165,9 +283,9 @@ func (d *imageDecorator) decorate(deployment *appsv1.Deployment) error {
return fmt.Errorf("registries value is invalid")
}

for i := range deployment.Spec.Template.Spec.Containers {
deployment.Spec.Template.Spec.Containers[i].Image = addonfactory.OverrideImage(
ims, deployment.Spec.Template.Spec.Containers[i].Image)
for i := range pod.Spec.Containers {
pod.Spec.Containers[i].Image = addonfactory.OverrideImage(
ims, pod.Spec.Containers[i].Image)
}

return nil
Expand Down
Loading
Loading