Skip to content

Commit

Permalink
fix(appset): Look for plugin generator config in the correct namespace
Browse files Browse the repository at this point in the history
Signed-off-by: OpenGuidou <[email protected]>
  • Loading branch information
OpenGuidou committed Dec 3, 2024
1 parent 52dbe51 commit c1c32f6
Show file tree
Hide file tree
Showing 13 changed files with 758 additions and 270 deletions.
27 changes: 18 additions & 9 deletions applicationset/generators/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App

providerConfig := appSetGenerator.Plugin

pluginClient, err := g.getPluginFromGenerator(ctx, applicationSetInfo.Name, providerConfig)
pluginConfigNamespace := g.getPluginConfigNamespaceOrDefaultToAppSetNamespace(providerConfig, applicationSetInfo)

pluginClient, err := g.getPluginFromGenerator(ctx, applicationSetInfo.Name, pluginConfigNamespace, providerConfig)
if err != nil {
return nil, fmt.Errorf("error getting plugin from generator: %w", err)
}
Expand All @@ -86,12 +88,19 @@ func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
return res, nil
}

func (g *PluginGenerator) getPluginFromGenerator(ctx context.Context, appSetName string, generatorConfig *argoprojiov1alpha1.PluginGenerator) (*plugin.Service, error) {
cm, err := g.getConfigMap(ctx, generatorConfig.ConfigMapRef.Name)
func (g *PluginGenerator) getPluginConfigNamespaceOrDefaultToAppSetNamespace(plugin *argoprojiov1alpha1.PluginGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) string {
if plugin.ConfigMapRef.Namespace != "" {
return plugin.ConfigMapRef.Namespace
}
return applicationSetInfo.Namespace
}

func (g *PluginGenerator) getPluginFromGenerator(ctx context.Context, appSetName, namespace string, generatorConfig *argoprojiov1alpha1.PluginGenerator) (*plugin.Service, error) {
cm, err := g.getConfigMap(ctx, generatorConfig.ConfigMapRef.Name, namespace)
if err != nil {
return nil, fmt.Errorf("error fetching ConfigMap: %w", err)
}
token, err := g.getToken(ctx, cm["token"])
token, err := g.getToken(ctx, cm["token"], namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Secret token: %w", err)
}
Expand Down Expand Up @@ -149,7 +158,7 @@ func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.App
return res, nil
}

func (g *PluginGenerator) getToken(ctx context.Context, tokenRef string) (string, error) {
func (g *PluginGenerator) getToken(ctx context.Context, tokenRef, namespace string) (string, error) {
if tokenRef == "" || !strings.HasPrefix(tokenRef, "$") {
return "", fmt.Errorf("token is empty, or does not reference a secret key starting with '$': %v", tokenRef)
}
Expand All @@ -161,11 +170,11 @@ func (g *PluginGenerator) getToken(ctx context.Context, tokenRef string) (string
ctx,
client.ObjectKey{
Name: secretName,
Namespace: g.namespace,
Namespace: namespace,
},
secret)
if err != nil {
return "", fmt.Errorf("error fetching secret %s/%s: %w", g.namespace, secretName, err)
return "", fmt.Errorf("error fetching secret %s/%s: %w", namespace, secretName, err)
}

secretValues := make(map[string]string, len(secret.Data))
Expand All @@ -179,13 +188,13 @@ func (g *PluginGenerator) getToken(ctx context.Context, tokenRef string) (string
return token, err
}

func (g *PluginGenerator) getConfigMap(ctx context.Context, configMapRef string) (map[string]string, error) {
func (g *PluginGenerator) getConfigMap(ctx context.Context, configMapRef, namespace string) (map[string]string, error) {
cm := &corev1.ConfigMap{}
err := g.client.Get(
ctx,
client.ObjectKey{
Name: configMapRef,
Namespace: g.namespace,
Namespace: namespace,
},
cm)
if err != nil {
Expand Down
94 changes: 92 additions & 2 deletions applicationset/generators/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func TestPluginGenerateParams(t *testing.T) {
name string
configmap *v1.ConfigMap
secret *v1.Secret
configNamespace string
inputParameters map[string]apiextensionsv1.JSON
values map[string]string
gotemplate bool
Expand Down Expand Up @@ -626,6 +627,94 @@ func TestPluginGenerateParams(t *testing.T) {
},
expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: token not found in ConfigMap"),
},
{
name: "Config in another namespace",
configmap: &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "first-plugin-cm",
Namespace: "other-ns",
},
Data: map[string]string{
"baseUrl": "http://127.0.0.1",
"token": "$plugin.token",
},
},
secret: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-secret",
Namespace: "other-ns",
},
Data: map[string][]byte{
"plugin.token": []byte("my-secret"),
},
},
configNamespace: "other-ns",
inputParameters: map[string]apiextensionsv1.JSON{
"pkey1": {Raw: []byte(`"val1"`)},
"pkey2": {Raw: []byte(`"val2"`)},
},
gotemplate: false,
content: []byte(`{"output": {
"parameters": [{
"key1": "val1",
"key2": {
"key2_1": "val2_1",
"key2_2": {
"key2_2_1": "val2_2_1"
}
},
"key3": 123
}]
}}`),
expected: []map[string]interface{}{
{
"key1": "val1",
"key2.key2_1": "val2_1",
"key2.key2_2.key2_2_1": "val2_2_1",
"key3": "123",
"generator": map[string]interface{}{
"input": argoprojiov1alpha1.PluginInput{
Parameters: argoprojiov1alpha1.PluginParameters{
"pkey1": {Raw: []byte(`"val1"`)},
"pkey2": {Raw: []byte(`"val2"`)},
},
},
},
},
},
expectedError: nil,
},
{
name: "Config in another namespace and not specified in the plugin generator",
configmap: &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "first-plugin-cm",
Namespace: "other-ns",
},
Data: map[string]string{
"baseUrl": "http://127.0.0.1",
"token": "$plugin.token",
},
},
secret: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-secret",
Namespace: "other-ns",
},
Data: map[string][]byte{
"plugin.token": []byte("my-secret"),
},
},
configNamespace: "",
inputParameters: map[string]apiextensionsv1.JSON{
"pkey1": {Raw: []byte(`"val1"`)},
"pkey2": {Raw: []byte(`"val2"`)},
},
gotemplate: false,
content: []byte{},
expected: []map[string]interface{}{},
expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: configmaps \"first-plugin-cm\" not found"),
},
}

ctx := context.Background()
Expand All @@ -634,7 +723,7 @@ func TestPluginGenerateParams(t *testing.T) {
t.Run(testCase.name, func(t *testing.T) {
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
Plugin: &argoprojiov1alpha1.PluginGenerator{
ConfigMapRef: argoprojiov1alpha1.PluginConfigMapRef{Name: testCase.configmap.Name},
ConfigMapRef: argoprojiov1alpha1.PluginConfigMapRef{Name: testCase.configmap.Name, Namespace: testCase.configNamespace},
Input: argoprojiov1alpha1.PluginInput{
Parameters: testCase.inputParameters,
},
Expand Down Expand Up @@ -674,7 +763,8 @@ func TestPluginGenerateParams(t *testing.T) {

applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
Name: "set",
Namespace: "default",
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{
GoTemplate: testCase.gotemplate,
Expand Down
4 changes: 4 additions & 0 deletions assets/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion docs/operator-manual/applicationset/Appset-Any-Namespace.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,18 @@ spec:
- clusters: {} # Automatically use all clusters defined within Argo CD
```

If you don't want to allow users to discover all clusters with ApplicationSets from other namespaces you may consider deploying ArgoCD in namespace scope or use OPA rules.
If you don't want to allow users to discover all clusters with ApplicationSets from other namespaces you may consider deploying ArgoCD in namespace scope or use OPA rules.

## Plugin generators consideration

When using plugin generators with ApplicationSet in any namespace, the plugin ConfigMap and Secret are expected to be in the same namespace as the ApplicationSet.
If not, you can specified a different namespace in the `configMapRef` `namespace` field. This namespace should be of course allowed in your setup.

```yaml
spec:
generators:
- plugin:
configMapRef:
name: "my-plugin-cm"
namespace: "my-namespace"
```
9 changes: 9 additions & 0 deletions manifests/core-install.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions manifests/crds/applicationset-crd.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions manifests/ha/install.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions manifests/install.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/apis/application/v1alpha1/applicationset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,8 @@ type PullRequestGeneratorFilter struct {
type PluginConfigMapRef struct {
// Name of the ConfigMap
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// Namespace of the ConfigMap
Namespace string `json:"namespace" protobuf:"bytes,2,opt,name=namespace"`
}

type PluginParameters map[string]apiextensionsv1.JSON
Expand Down
Loading

0 comments on commit c1c32f6

Please sign in to comment.