metio / terraform-provider-k8s

Terraform provider for Kubernetes resources
https://registry.terraform.io/providers/metio/k8s/
BSD Zero Clause License
6 stars 2 forks source link

[Bug]: kyverno_io_cluster_policy_v1_manifest - spec.rules.context.api_call.data.value is a map of strings #169

Open calvinbui opened 1 month ago

calvinbui commented 1 month ago

Relevant Terraform configuration

{
  name = "response"
  api_call = {
    method = "POST"

    data = [
      {
        key   = "images"
        value = "{{images}}"
      },
    ]
  }
}

Relevant log output

Inappropriate value for attribute "spec": attribute "rules": element 0:
attribute "context": element 1: attribute "api_call": attribute "data":
element 0: attribute "value": map of string required.

Operation failed: failed running terraform plan (exit 1)
calvinbui commented 1 month ago

The issue is that spec.rules.context.api_call.data.value is expected to be a map of strings.

However, with how Kyverno has coded it, it can be a string, a list or a map: https://github.com/kyverno/kyverno/blob/279895c60056b1552476663b0fa814cb0e7d7597/api/kyverno/v1/common_types.go#L235

Simply, a string is passed and then converted using the JSON module.

Sample code, you can replace {"service": "something"} with a bool, int, string, etc.

package main

import (
    "encoding/json"
    "fmt"

    apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)

type RequestData struct {
    // Key is a unique identifier for the data value
    Key string `json:"key" yaml:"key"`

    // Value is the data value
    Value *apiextv1.JSON `json:"value" yaml:"value"`
}

func main() {
    data := []RequestData{
        {
            Key: "imageReferences",
            Value: &apiextv1.JSON{
                Raw: []byte(`{"service": "something"}`), // Notice the backticks for a valid JSON string
            },
        },
    }

    dataMap := make(map[string]interface{})
    for _, d := range data {
        var value interface{}
        if err := json.Unmarshal(d.Value.Raw, &value); err != nil {
            fmt.Printf("Error unmarshaling JSON for key %s: %v\n", d.Key, err)
            continue
        }
        dataMap[d.Key] = value
    }

    for key, value := range dataMap {
        fmt.Printf("%s: %v\n", key, value)
    }
}

I'm not sure what the best solution is.

calvinbui commented 1 month ago

The CRD however is an object:

❯ kubectl explain clusterpolicies.spec.rules.context.apiCall.data.value
GROUP:      kyverno.io
KIND:       ClusterPolicy
VERSION:    v1

FIELD: value <Object>

DESCRIPTION:
    Value is the data value

Other examples from Kyverno where it is a string and a map