diff --git a/manifest/parse.go b/manifest/parse.go index 17db6abf..49a0143a 100644 --- a/manifest/parse.go +++ b/manifest/parse.go @@ -99,31 +99,19 @@ func Parse(manifest string, defaultNamespace string, excludedHooks ...string) ma if content == "" { continue } - var parsedMetadata metadata - if err := yaml.Unmarshal([]byte(content), &parsedMetadata); err != nil { - log.Fatalf("YAML unmarshal error: %s\nCan't unmarshal %s", err, content) - } - // Skip content without any metadata. It is probably a template that - // only contains comments in the current state. - if parsedMetadata.APIVersion == "" && parsedMetadata.Kind == "" { - continue - } - if isHook(parsedMetadata, excludedHooks...) { - continue + parsed, err := parseContent(content, defaultNamespace, excludedHooks...) + if err != nil { + log.Fatalf("%v", err) } - if parsedMetadata.Metadata.Namespace == "" { - parsedMetadata.Metadata.Namespace = defaultNamespace - } - name := parsedMetadata.String() - if _, ok := result[name]; ok { - log.Printf("Error: Found duplicate key %#v in manifest", name) - } else { - result[name] = &MappingResult{ - Name: name, - Kind: parsedMetadata.Kind, - Content: content, + for _, p := range parsed { + name := p.Name + + if _, ok := result[name]; ok { + log.Printf("Error: Found duplicate key %#v in manifest", name) + } else { + result[name] = p } } } @@ -133,6 +121,66 @@ func Parse(manifest string, defaultNamespace string, excludedHooks ...string) ma return result } +func parseContent(content string, defaultNamespace string, excludedHooks ...string) ([]*MappingResult, error) { + var parsedMetadata metadata + if err := yaml.Unmarshal([]byte(content), &parsedMetadata); err != nil { + log.Fatalf("YAML unmarshal error: %s\nCan't unmarshal %s", err, content) + } + + // Skip content without any metadata. It is probably a template that + // only contains comments in the current state. + if parsedMetadata.APIVersion == "" && parsedMetadata.Kind == "" { + return nil, nil + } + + if parsedMetadata.Kind == "List" { + type ListV1 struct { + Items []yaml.MapSlice `yaml:"items"` + } + + var list ListV1 + + if err := yaml.Unmarshal([]byte(content), &list); err != nil { + log.Fatalf("YAML unmarshal error: %s\nCan't unmarshal %s", err, content) + } + + var result []*MappingResult + + for _, item := range list.Items { + subcontent, err := yaml.Marshal(item) + if err != nil { + log.Printf("YAML marshal error: %s\nCan't marshal %v", err, item) + } + + subs, err := parseContent(string(subcontent), defaultNamespace, excludedHooks...) + if err != nil { + return nil, fmt.Errorf("Parsing YAML list item: %v", err) + } + + result = append(result, subs...) + } + + return result, nil + } + + if isHook(parsedMetadata, excludedHooks...) { + return nil, nil + } + + if parsedMetadata.Metadata.Namespace == "" { + parsedMetadata.Metadata.Namespace = defaultNamespace + } + + name := parsedMetadata.String() + return []*MappingResult{ + { + Name: name, + Kind: parsedMetadata.Kind, + Content: content, + }, + }, nil +} + func isHook(metadata metadata, hooks ...string) bool { for _, hook := range hooks { if metadata.Metadata.Annotations[hookAnnotation] == hook { diff --git a/manifest/parse_test.go b/manifest/parse_test.go index 731b5bc2..3acffc1f 100644 --- a/manifest/parse_test.go +++ b/manifest/parse_test.go @@ -79,6 +79,19 @@ func TestDeployV1Beta1(t *testing.T) { ) } +func TestList(t *testing.T) { + spec, err := ioutil.ReadFile("testdata/list.yaml") + require.NoError(t, err) + + require.Equal(t, + []string{ + "default, prometheus-operator-example, PrometheusRule (monitoring.coreos.com)", + "default, prometheus-operator-example2, PrometheusRule (monitoring.coreos.com)", + }, + foundObjects(Parse(string(spec), "default")), + ) +} + func TestEmpty(t *testing.T) { spec, err := ioutil.ReadFile("testdata/empty.yaml") require.NoError(t, err) diff --git a/manifest/testdata/list.yaml b/manifest/testdata/list.yaml new file mode 100644 index 00000000..07690fc5 --- /dev/null +++ b/manifest/testdata/list.yaml @@ -0,0 +1,50 @@ + +--- +# Source: prometheus-operator/templates/prometheus/additionalPrometheusRules.yaml +apiVersion: v1 +kind: List +items: + - apiVersion: monitoring.coreos.com/v1 + kind: PrometheusRule + metadata: + name: prometheus-operator-example + namespace: default + labels: + app: prometheus-operator + chart: prometheus-operator-9.3.0 + release: "foo" + heritage: "Helm" + spec: + groups: + - name: mygroup + rules: + - annotations: + summary: Container {{ $labels.container }} in Pod {{$labels.namespace}}/{{$labels.pod}} + restarting + expr: count(sum by (pod)(delta(kube_pod_container_status_restarts_total[15m]) + > 0)) + labels: + severity: warning + record: ContainerRestarted + - apiVersion: monitoring.coreos.com/v1 + kind: PrometheusRule + metadata: + name: prometheus-operator-example2 + namespace: default + labels: + app: prometheus-operator + chart: prometheus-operator-9.3.0 + release: "foo" + heritage: "Helm" + spec: + groups: + - name: mygroup2 + rules: + - annotations: + summary: Container {{ $labels.container }} in Pod {{$labels.namespace}}/{{$labels.pod}} + restarting + expr: count(sum by (pod)(delta(kube_pod_container_status_restarts_total[15m]) + > 0)) + labels: + severity: warning + record: ContainerRestarted