Skip to content

Commit

Permalink
Set install namespace of addonTemplate from config
Browse files Browse the repository at this point in the history
Signed-off-by: Jian Qiu <jqiu@redhat.com>
  • Loading branch information
qiujian16 committed Apr 16, 2024
1 parent f413220 commit 9737759
Show file tree
Hide file tree
Showing 7 changed files with 352 additions and 60 deletions.
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
165 changes: 142 additions & 23 deletions pkg/addon/templateagent/decorator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,147 @@ 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(configValues addonfactory.Values) *namespaceDecorator {
decorator := &namespaceDecorator{
paths: map[string]string{
"ClusterRoleBinding": "subjects",
"RoleBinding": "subjects",
},
}
namespace, ok := configValues["INSTALL_NAMESPACE"]
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
}

// If obj has no namespace set, we do not mutate namespace assuming it is cluster scoped.
if len(obj.GetNamespace()) > 0 {
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(obj, 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 +153,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 +167,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 +221,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 +235,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 +253,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 +267,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 +284,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

0 comments on commit 9737759

Please sign in to comment.