Skip to content

Commit 00797ea

Browse files
authored
INS-1519: Fix kyverno policy download (#285)
* Fix YAML convertion * Fixes
1 parent 9038472 commit 00797ea

5 files changed

Lines changed: 126 additions & 3 deletions

File tree

pkg/appgroups/types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package appgroups
22

3+
import "github.com/fairwindsops/insights-cli/pkg/utils"
4+
35
// copied from FW Insights openAPI codegen
46

57
type AppGroup struct {
@@ -8,6 +10,10 @@ type AppGroup struct {
810
Type string `json:"type,omitempty" yaml:"type,omitempty"`
911
}
1012

13+
func (a AppGroup) GetYamlBytes() ([]byte, error) {
14+
return utils.GetYamlBytes(a)
15+
}
16+
1117
// AppGroupSpec defines model for AppGroupSpec.
1218
type AppGroupSpec struct {
1319
Exclude []AppGroupSpecCriteria `json:"exclude,omitempty" yaml:"exclude,omitempty"`

pkg/cli/download.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222

2323
"github.com/sirupsen/logrus"
2424
"github.com/spf13/cobra"
25-
"gopkg.in/yaml.v3"
2625
)
2726

2827
var downloadDir string
@@ -50,6 +49,7 @@ var downloadCmd = &cobra.Command{
5049

5150
type nameable interface {
5251
GetName() string
52+
GetYamlBytes() ([]byte, error)
5353
}
5454

5555
var filenameRegex = regexp.MustCompile("[^A-Za-z0-9]+")
@@ -78,9 +78,9 @@ func saveEntitiesLocally[T nameable](saveDir string, entities []T, overrideLocal
7878
filename := formatFilename(e.GetName())
7979
filePath := saveDir + "/" + filename
8080

81-
b, err := yaml.Marshal(e)
81+
b, err := e.GetYamlBytes()
8282
if err != nil {
83-
return saved, fmt.Errorf("error marshalling policy-mapping %s: %w", e.GetName(), err)
83+
return saved, fmt.Errorf("error getting yaml bytes for entity %s: %w", e.GetName(), err)
8484
}
8585
err = os.WriteFile(filePath, b, 0644)
8686
if err != nil {

pkg/kyverno/types.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
package kyverno
1616

1717
import (
18+
"bytes"
19+
"fmt"
20+
"strings"
1821
"time"
22+
23+
"go.yaml.in/yaml/v3"
1924
)
2025

2126
// KyvernoPolicy represents a Kyverno policy
@@ -34,6 +39,102 @@ type KyvernoPolicy struct {
3439
UpdatedAt *time.Time `json:"updated_at,omitempty"`
3540
}
3641

42+
func (k KyvernoPolicy) GetYamlBytes() ([]byte, error) {
43+
yamlBytes, err := convertPolicySpecToYAML(k)
44+
if err != nil {
45+
return nil, fmt.Errorf("failed to convert policy spec to YAML: %w", err)
46+
}
47+
return []byte(yamlBytes), nil
48+
}
49+
50+
// Helper function to convert a KyvernoPolicy spec to YAML string
51+
func convertPolicySpecToYAML(policy KyvernoPolicy) (string, error) {
52+
// Parse the spec JSON
53+
54+
apiVersion := "kyverno.io/v1"
55+
kind := "ClusterPolicy"
56+
if policy.Kind != "" {
57+
kind = policy.Kind
58+
}
59+
if policy.APIVersion != "" {
60+
apiVersion = policy.APIVersion
61+
}
62+
// Create the full policy structure
63+
policyMap := map[string]any{
64+
"apiVersion": apiVersion,
65+
"kind": kind,
66+
"metadata": map[string]any{
67+
"name": policy.Name,
68+
},
69+
"spec": policy.Spec,
70+
}
71+
72+
// Add labels and annotations if they exist and are not empty
73+
if len(policy.Labels) > 0 {
74+
policyMap["metadata"].(map[string]any)["labels"] = policy.Labels
75+
}
76+
77+
// Add existing annotations if they exist
78+
if len(policy.Annotations) > 0 {
79+
policyMap["metadata"].(map[string]any)["annotations"] = policy.Annotations
80+
}
81+
82+
// Add status only if it exists and is not null
83+
if len(policy.Status) > 0 {
84+
policyMap["status"] = policy.Status
85+
}
86+
// Clean up any null values before marshaling
87+
cleanPolicyMap := cleanNullValues(policyMap)
88+
89+
// Convert to YAML
90+
// yaml converter should use only 2 spaces for indentation
91+
var buf bytes.Buffer
92+
encoder := yaml.NewEncoder(&buf)
93+
encoder.SetIndent(2)
94+
err := encoder.Encode(cleanPolicyMap)
95+
if err != nil {
96+
return "", fmt.Errorf("failed to marshal policy to YAML: %w", err)
97+
}
98+
yamlContent := buf.String()
99+
yamlContent = strings.TrimSpace(yamlContent)
100+
return yamlContent, nil
101+
}
102+
103+
// cleanNullValues recursively removes null values from a map
104+
func cleanNullValues(data any) any {
105+
switch v := data.(type) {
106+
case map[string]any:
107+
cleaned := make(map[string]any)
108+
for key, value := range v {
109+
if value != nil {
110+
cleanedValue := cleanNullValues(value)
111+
if cleanedValue != nil {
112+
cleaned[key] = cleanedValue
113+
}
114+
}
115+
}
116+
return cleaned
117+
case []any:
118+
var cleaned []any
119+
for _, item := range v {
120+
if item != nil {
121+
cleanedItem := cleanNullValues(item)
122+
if cleanedItem != nil {
123+
cleaned = append(cleaned, cleanedItem)
124+
}
125+
}
126+
}
127+
return cleaned
128+
case nil:
129+
return nil
130+
case string:
131+
// Don't process strings, return as-is
132+
return v
133+
default:
134+
return v
135+
}
136+
}
137+
37138
// GetName implements the nameable interface for download functionality
38139
func (k KyvernoPolicy) GetName() string {
39140
return k.Name

pkg/policymappings/types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package policymappings
22

3+
import (
4+
"github.com/fairwindsops/insights-cli/pkg/utils"
5+
)
6+
37
// copied from FW Insights openAPI codegen
48

59
type PolicyMapping struct {
@@ -19,3 +23,7 @@ type PolicyMappingSpec struct {
1923
func (pm PolicyMapping) GetName() string {
2024
return pm.Name
2125
}
26+
27+
func (pm PolicyMapping) GetYamlBytes() ([]byte, error) {
28+
return utils.GetYamlBytes(pm)
29+
}

pkg/utils/utils.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package utils
22

3+
import (
4+
"go.yaml.in/yaml/v2"
5+
)
6+
37
func GetHeaders(inputContentType string) map[string]string {
48
contentType := "application/json"
59
if inputContentType != "" {
@@ -20,3 +24,7 @@ func InvertBoolPointer(b *bool) *bool {
2024
r := !*b
2125
return &r
2226
}
27+
28+
func GetYamlBytes[T any](e T) ([]byte, error) {
29+
return yaml.Marshal(e)
30+
}

0 commit comments

Comments
 (0)