mirror of
https://github.com/yannh/kubeconform.git
synced 2026-04-14 00:14:45 +00:00
support for lists
This commit is contained in:
parent
fa1cb37020
commit
4eb75860d9
8 changed files with 162 additions and 49 deletions
|
|
@ -94,8 +94,11 @@ func findResourcesInReader(p string, f io.Reader, resources chan<- Resource, err
|
|||
nRes := 0
|
||||
for scanner.Scan() {
|
||||
if len(scanner.Text()) > 0 {
|
||||
resources <- Resource{Path: p, Bytes: []byte(scanner.Text())}
|
||||
nRes++
|
||||
res := Resource{Path: p, Bytes: []byte(scanner.Text())}
|
||||
for _, subres := range res.Resources() {
|
||||
resources <- subres
|
||||
nRes++
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
|
|
|
|||
|
|
@ -2,15 +2,17 @@ package resource
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// Resource represents a Kubernetes resource within a file
|
||||
type Resource struct {
|
||||
Path string
|
||||
Bytes []byte
|
||||
sig *Signature
|
||||
Path string
|
||||
Bytes []byte
|
||||
sig *Signature // Cache signature parsing
|
||||
sigErr error // Cache potential signature parsing error
|
||||
}
|
||||
|
||||
// Signature is a key representing a Kubernetes resource
|
||||
|
|
@ -21,7 +23,7 @@ type Signature struct {
|
|||
// Signature computes a signature for a resource, based on its Kind, Version, Namespace & Name
|
||||
func (res *Resource) Signature() (*Signature, error) {
|
||||
if res.sig != nil {
|
||||
return res.sig, nil
|
||||
return res.sig, res.sigErr
|
||||
}
|
||||
|
||||
resource := struct {
|
||||
|
|
@ -44,33 +46,38 @@ func (res *Resource) Signature() (*Signature, error) {
|
|||
res.sig = &Signature{Kind: resource.Kind, Version: resource.APIVersion, Namespace: resource.Metadata.Namespace, Name: name}
|
||||
|
||||
if err != nil { // Exit if there was an error unmarshalling
|
||||
return res.sig, err
|
||||
res.sigErr = err
|
||||
return res.sig, res.sigErr
|
||||
}
|
||||
|
||||
if resource.Kind == "" {
|
||||
return res.sig, fmt.Errorf("missing 'kind' key")
|
||||
if res.sig.Kind == "" {
|
||||
res.sigErr = fmt.Errorf("missing 'kind' key")
|
||||
return res.sig, res.sigErr
|
||||
}
|
||||
|
||||
if resource.APIVersion == "" {
|
||||
return res.sig, fmt.Errorf("missing 'apiVersion' key")
|
||||
if res.sig.Version == "" {
|
||||
res.sigErr = fmt.Errorf("missing 'apiVersion' key")
|
||||
return res.sig, res.sigErr
|
||||
}
|
||||
|
||||
return res.sig, err
|
||||
return res.sig, res.sigErr
|
||||
}
|
||||
|
||||
func (res *Resource) SignatureFromMap(m map[string]interface{}) (*Signature, error) {
|
||||
if res.sig != nil {
|
||||
return res.sig, nil
|
||||
return res.sig, res.sigErr
|
||||
}
|
||||
|
||||
Kind, ok := m["kind"].(string)
|
||||
if !ok {
|
||||
return res.sig, fmt.Errorf("missing 'kind' key")
|
||||
res.sigErr = fmt.Errorf("missing 'kind' key")
|
||||
return res.sig, res.sigErr
|
||||
}
|
||||
|
||||
APIVersion, ok := m["apiVersion"].(string)
|
||||
if !ok {
|
||||
return res.sig, fmt.Errorf("missing 'apiVersion' key")
|
||||
res.sigErr = fmt.Errorf("missing 'apiVersion' key")
|
||||
return res.sig, res.sigErr
|
||||
}
|
||||
|
||||
var name, ns string
|
||||
|
|
@ -87,3 +94,28 @@ func (res *Resource) SignatureFromMap(m map[string]interface{}) (*Signature, err
|
|||
res.sig = &Signature{Kind: Kind, Version: APIVersion, Namespace: ns, Name: name}
|
||||
return res.sig, nil
|
||||
}
|
||||
|
||||
// Resources returns a list of resources if the resource is of type List, a single resource otherwise
|
||||
// See https://github.com/yannh/kubeconform/issues/53
|
||||
func (res *Resource) Resources() []Resource {
|
||||
resources := []Resource{}
|
||||
if s, err := res.Signature(); err == nil && strings.ToLower(s.Kind) == "list" {
|
||||
// A single file of type List
|
||||
list := struct {
|
||||
Version string
|
||||
Kind string
|
||||
Items []interface{}
|
||||
}{}
|
||||
|
||||
yaml.Unmarshal(res.Bytes, &list)
|
||||
|
||||
for _, item := range list.Items {
|
||||
r := Resource{Path: res.Path}
|
||||
r.Bytes, _ = yaml.Marshal(item)
|
||||
resources = append(resources, r)
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
return []Resource{*res}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,3 +86,39 @@ func TestSignatureFromMap(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResources(t *testing.T) {
|
||||
testCases := []struct {
|
||||
b string
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
"apiVersion: v1\nkind: List\n",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"apiVersion: v1\nkind: List\nItems: []\n",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"apiVersion: v1\nkind: List\nItems:\n - apiVersion: v1\n kind: ReplicationController\n metadata:\n name: \"bob\"\n spec:\n replicas: 2\n",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"apiVersion: v1\nkind: List\nItems:\n - apiVersion: v1\n kind: ReplicationController\n metadata:\n name: \"bob\"\n spec:\n replicas: 2\n - apiVersion: v1\n kind: ReplicationController\n metadata:\n name: \"Jim\"\n spec:\n replicas: 2\n",
|
||||
2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
res := resource.Resource{
|
||||
Path: "foo",
|
||||
Bytes: []byte(testCase.b),
|
||||
}
|
||||
|
||||
subres := res.Resources()
|
||||
if len(subres) != testCase.expected {
|
||||
t.Errorf("test %d: expected to find %d resources, found %d", i, testCase.expected, len(subres))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,10 @@ func FromStream(ctx context.Context, path string, r io.Reader) (<-chan Resource,
|
|||
break SCAN
|
||||
default:
|
||||
}
|
||||
resources <- Resource{Path: path, Bytes: scanner.Bytes()}
|
||||
res := Resource{Path: path, Bytes: scanner.Bytes()}
|
||||
for _, subres := range res.Resources() {
|
||||
resources <- subres
|
||||
}
|
||||
}
|
||||
|
||||
close(resources)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue