diff --git a/cue.mod/module.cue b/cue.mod/module.cue new file mode 100644 index 0000000..7e34dbf --- /dev/null +++ b/cue.mod/module.cue @@ -0,0 +1,4 @@ +module: "github.com/mpv/kir" +language: { + version: "v0.9.0" +} diff --git a/cueparser/cueparser.go b/cueparser/cueparser.go new file mode 100644 index 0000000..56b6397 --- /dev/null +++ b/cueparser/cueparser.go @@ -0,0 +1,135 @@ +package cueparser + +import ( + "fmt" + "strings" + + "cuelang.org/go/cue" + "cuelang.org/go/cue/cuecontext" + "cuelang.org/go/cue/load" + "cuelang.org/go/encoding/yaml" +) + +// ProcessData processes YAML data and extracts container images from PodSpec +func ProcessData(data []byte) ([]string, error) { + // Create a CUE context + ctx := cuecontext.New() + + // Load the PodSpec schema from the CUE Central Registry + bis := load.Instances([]string{"cue.dev/x/k8s.io/api/core/v1"}, nil) + if len(bis) == 0 { + return nil, fmt.Errorf("failed to load PodSpec schema from Central Registry") + } + + pkgV := ctx.BuildInstance(bis[0]) + if pkgV.Err() != nil { + return nil, fmt.Errorf("failed to build PodSpec schema: %v", pkgV.Err()) + } + + podSpec := pkgV.LookupPath(cue.ParsePath("#PodSpec")) + if podSpec.Err() != nil { + return nil, fmt.Errorf("failed to lookup PodSpec: %v", podSpec.Err()) + } + + // Load the YAML data + dataV, err := yaml.Extract("", data) + if err != nil { + return nil, fmt.Errorf("failed to extract YAML: %v", err) + } + + dataValue := ctx.BuildFile(dataV) + if dataValue.Err() != nil { + return nil, fmt.Errorf("failed to build YAML data: %v", dataValue.Err()) + } + + // Unify the YAML value with the schema and validate + combined := podSpec.Unify(dataValue) + if err := combined.Validate(cue.Concrete(true)); err != nil { + return nil, fmt.Errorf("validation error: %v", err) + } + + // Extract container images + var images []string + + // Try to get containers from the validated PodSpec + containersValue := combined.LookupPath(cue.ParsePath("containers")) + if containersValue.Exists() { + // Iterate through containers + iter, err := containersValue.List() + if err != nil { + return nil, fmt.Errorf("failed to iterate containers: %v", err) + } + + for iter.Next() { + container := iter.Value() + imageValue := container.LookupPath(cue.ParsePath("image")) + if imageValue.Exists() { + image, err := imageValue.String() + if err != nil { + return nil, fmt.Errorf("failed to get image string: %v", err) + } + images = append(images, image) + } + } + } + + // Try to get initContainers from the validated PodSpec + initContainersValue := combined.LookupPath(cue.ParsePath("initContainers")) + if initContainersValue.Exists() { + // Iterate through initContainers + iter, err := initContainersValue.List() + if err != nil { + return nil, fmt.Errorf("failed to iterate initContainers: %v", err) + } + + for iter.Next() { + container := iter.Value() + imageValue := container.LookupPath(cue.ParsePath("image")) + if imageValue.Exists() { + image, err := imageValue.String() + if err != nil { + return nil, fmt.Errorf("failed to get image string: %v", err) + } + images = append(images, image) + } + } + } + + return images, nil +} + +// ProcessKubernetesYAML processes a Kubernetes YAML document and extracts container images +func ProcessKubernetesYAML(data []byte) ([]string, error) { + // Try to extract the PodSpec directly + images, err := ProcessData(data) + if err == nil && len(images) > 0 { + return images, nil + } + + // If that fails, return the error + return nil, fmt.Errorf("failed to extract PodSpec: %v", err) +} + +// ProcessKubernetesListYAML processes a Kubernetes List YAML document and extracts container images +func ProcessKubernetesListYAML(data []byte) ([]string, error) { + // Split the YAML document by "---" to handle multiple resources + docs := strings.Split(string(data), "---") + + var allImages []string + for _, doc := range docs { + if strings.TrimSpace(doc) == "" { + continue + } + + images, err := ProcessKubernetesYAML([]byte(doc)) + if err != nil { + // Log the error but continue processing other documents + fmt.Printf("Error processing document: %v\n", err) + continue + } + + allImages = append(allImages, images...) + } + + return allImages, nil +} diff --git a/cueparser/cueparser_test.go b/cueparser/cueparser_test.go new file mode 100644 index 0000000..7b0dcf1 --- /dev/null +++ b/cueparser/cueparser_test.go @@ -0,0 +1,138 @@ +package cueparser + +import ( + "testing" +) + +func TestProcessData(t *testing.T) { + tests := []struct { + name string + data []byte + want []string + wantErr bool + }{ + { + name: "Valid PodSpec with containers", + data: []byte(` +containers: + - name: test-container + image: test-image +`), + want: []string{"test-image"}, + wantErr: false, + }, + { + name: "Valid PodSpec with initContainers", + data: []byte(` +initContainers: + - name: init-container + image: init-image +`), + want: []string{"init-image"}, + wantErr: false, + }, + { + name: "Valid PodSpec with both containers and initContainers", + data: []byte(` +containers: + - name: test-container + image: test-image +initContainers: + - name: init-container + image: init-image +`), + want: []string{"test-image", "init-image"}, + wantErr: false, + }, + { + name: "Invalid PodSpec (missing required fields)", + data: []byte(` +containers: + - image: test-image +`), + want: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ProcessData(tt.data) + if (err != nil) != tt.wantErr { + t.Errorf("ProcessData() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if len(got) != len(tt.want) { + t.Errorf("ProcessData() got %v images, want %v", len(got), len(tt.want)) + return + } + for i, img := range got { + if img != tt.want[i] { + t.Errorf("ProcessData() got image %v, want %v", img, tt.want[i]) + } + } + } + }) + } +} + +func TestProcessKubernetesListYAML(t *testing.T) { + tests := []struct { + name string + data []byte + want []string + wantErr bool + }{ + { + name: "Multiple PodSpecs", + data: []byte(` +--- +containers: + - name: container1 + image: image1 +--- +containers: + - name: container2 + image: image2 +`), + want: []string{"image1", "image2"}, + wantErr: false, + }, + { + name: "Mixed valid and invalid PodSpecs", + data: []byte(` +--- +containers: + - name: container1 + image: image1 +--- +containers: + - image: image2 +`), + want: []string{"image1"}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ProcessKubernetesListYAML(tt.data) + if (err != nil) != tt.wantErr { + t.Errorf("ProcessKubernetesListYAML() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if len(got) != len(tt.want) { + t.Errorf("ProcessKubernetesListYAML() got %v images, want %v", len(got), len(tt.want)) + return + } + for i, img := range got { + if img != tt.want[i] { + t.Errorf("ProcessKubernetesListYAML() got image %v, want %v", img, tt.want[i]) + } + } + } + }) + } +} diff --git a/go.mod b/go.mod index a9e1f85..fa0151f 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/mpv/kir go 1.24.1 require ( + cuelang.org/go v0.12.0 github.com/approvals/go-approval-tests v1.2.0 k8s.io/api v0.32.3 k8s.io/apimachinery v0.32.3 @@ -10,18 +11,30 @@ require ( ) require ( + cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1 // indirect + github.com/cockroachdb/apd/v3 v3.2.1 // indirect + github.com/emicklei/proto v1.13.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kr/text v0.2.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d // indirect + github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/net v0.30.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect diff --git a/go.sum b/go.sum index aa88e5c..c08f982 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,23 @@ +cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1 h1:mRwydyTyhtRX2wXS3mqYWzR2qlv6KsmoKXmlz5vInjg= +cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg= +cuelang.org/go v0.12.0 h1:q4W5I+RtDIA27rslQyyt6sWkXX0YS9qm43+U1/3e0kU= +cuelang.org/go v0.12.0/go.mod h1:B4+kjvGGQnbkz+GuAv1dq/R308gTkp0sO28FdMrJ2Kw= github.com/approvals/go-approval-tests v1.2.0 h1:0gnbtkFLryEa7ojI6YSP5SeBHOfYHCWFnOTM8q9BQz4= github.com/approvals/go-approval-tests v1.2.0/go.mod h1:Jw45q7bEifPpUkRU2S5GCthnwDxMUWPHTGcNewVh6mw= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/proto v1.13.4 h1:myn1fyf8t7tAqIzV91Tj9qXpvyXXGXk8OS2H6IBSc9g= +github.com/emicklei/proto v1.13.4/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -17,6 +26,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -25,16 +36,30 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq7lB31IeJL8iy7jkUmU/PG1Sr8jVGhS749dbUA= +github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= +github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a h1:w3tdWGKbLGBPtR/8/oO74W6hmz0qE5q0z9aqSAewaaM= +github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a/go.mod h1:S8kfXMp+yh77OxPD4fdM6YUknrZpQxLhvxzS4gDHENY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -50,26 +75,36 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/yamlparser/yamlparser.go b/yamlparser/yamlparser.go index b25dd53..2340865 100644 --- a/yamlparser/yamlparser.go +++ b/yamlparser/yamlparser.go @@ -1,9 +1,11 @@ package yamlparser import ( + "bytes" "fmt" "slices" + "github.com/mpv/kir/cueparser" "github.com/mpv/kir/k8s" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -13,7 +15,15 @@ import ( var supportedKinds = []string{"Pod", "Deployment", "DaemonSet", "ReplicaSet", "StatefulSet", "Job", "CronJob"} +// ProcessData processes YAML data and extracts container images func ProcessData(data []byte) ([]string, error) { + // First, try to use the CUE-based parser + cueImages, err := cueparser.ProcessData(data) + if err == nil && len(cueImages) > 0 { + return cueImages, nil + } + + // If CUE parsing fails, fall back to the original implementation // Decode the YAML file into a Kubernetes object decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode obj, gvk, err := decode(data, nil, nil) @@ -21,12 +31,12 @@ func ProcessData(data []byte) ([]string, error) { return nil, err } - var images []string + var k8sImages []string // Check if the object has containers if containers, err := k8s.GetContainersFromObject(obj); err == nil { - images = append(images, k8s.GetContainerImages(containers)...) - return images, nil + k8sImages = append(k8sImages, k8s.GetContainerImages(containers)...) + return k8sImages, nil } // Handle List type separately @@ -44,9 +54,9 @@ func ProcessData(data []byte) ([]string, error) { if err != nil { return nil, fmt.Errorf("error processing unstructured item: %v", err) } - images = append(images, imgs...) + k8sImages = append(k8sImages, imgs...) } - return images, nil + return k8sImages, nil } return nil, fmt.Errorf("unsupported kind %s", gvk.Kind) @@ -67,3 +77,26 @@ func processUnstructured(item unstructured.Unstructured) ([]string, error) { } return nil, fmt.Errorf("error: unsupported kind %s in List", gvk.Kind) } + +// ProcessKubernetesListYAML processes a Kubernetes List YAML document and extracts container images +func ProcessKubernetesListYAML(data []byte) ([]string, error) { + // First, try to use the CUE-based parser + cueImages, err := cueparser.ProcessKubernetesListYAML(data) + if err == nil && len(cueImages) > 0 { + return cueImages, nil + } + + // If CUE parsing fails, fall back to the original implementation + var k8sImages []string + docs := bytes.Split(data, []byte("\n---\n")) + for _, doc := range docs { + imgs, err := ProcessData(doc) + if err != nil { + // Log the error but continue processing other documents + fmt.Printf("Error processing document: %v\n", err) + continue + } + k8sImages = append(k8sImages, imgs...) + } + return k8sImages, nil +}