From 837b24bf54882823732ffcdb2006bf82e65edc18 Mon Sep 17 00:00:00 2001 From: Markus Rudy Date: Thu, 21 Dec 2023 20:56:55 +0100 Subject: [PATCH] versions: generate k8s image patches (incl etcd) (#2764) * versions: generate k8s image patches (incl etcd) --- bazel/toolchains/go_module_deps.bzl | 28 +- go.mod | 5 +- go.sum | 14 +- hack/go.mod | 4 +- hack/go.sum | 12 +- internal/versions/hash-generator/BUILD.bazel | 9 +- internal/versions/hash-generator/generate.go | 301 +++++++++++++------ internal/versions/versions.go | 12 + internal/versions/versions_test.go | 9 +- terraform-provider-constellation/go.mod | 2 +- terraform-provider-constellation/go.sum | 10 +- 11 files changed, 282 insertions(+), 124 deletions(-) diff --git a/bazel/toolchains/go_module_deps.bzl b/bazel/toolchains/go_module_deps.bzl index 08827d71dd..a14c9ffee8 100644 --- a/bazel/toolchains/go_module_deps.bzl +++ b/bazel/toolchains/go_module_deps.bzl @@ -1251,8 +1251,8 @@ def go_dependencies(): build_file_generation = "on", build_file_proto_mode = "disable_global", importpath = "github.com/cpuguy83/go-md2man/v2", - sum = "h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=", - version = "v2.0.2", + sum = "h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=", + version = "v2.0.3", ) go_repository( name = "com_github_creack_pty", @@ -1443,8 +1443,8 @@ def go_dependencies(): build_file_generation = "on", build_file_proto_mode = "disable_global", importpath = "github.com/docker/libtrust", - sum = "h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=", - version = "v0.0.0-20150114040149-fa567046d9b1", + sum = "h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=", + version = "v0.0.0-20160708172513-aabc10ec26b7", ) go_repository( name = "com_github_dustin_go_humanize", @@ -4347,6 +4347,14 @@ def go_dependencies(): sum = "h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=", version = "v9.0.5", ) + go_repository( + name = "com_github_regclient_regclient", + build_file_generation = "on", + build_file_proto_mode = "disable_global", + importpath = "github.com/regclient/regclient", + sum = "h1:fDh5afBCRbeSU71vcvbN0fI88D1fTfp2m39BPqki7EU=", + version = "v0.5.5", + ) go_repository( name = "com_github_rivo_uniseg", build_file_generation = "on", @@ -4704,8 +4712,8 @@ def go_dependencies(): build_file_generation = "on", build_file_proto_mode = "disable_global", importpath = "github.com/spf13/cobra", - sum = "h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=", - version = "v1.7.0", + sum = "h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=", + version = "v1.8.0", ) go_repository( name = "com_github_spf13_jwalterweatherman", @@ -5132,6 +5140,14 @@ def go_dependencies(): sum = "h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=", version = "v1.4.13", ) + go_repository( + name = "com_github_yuin_gopher_lua", + build_file_generation = "on", + build_file_proto_mode = "disable_global", + importpath = "github.com/yuin/gopher-lua", + sum = "h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=", + version = "v1.1.0", + ) go_repository( name = "com_github_yvasiyarov_go_metrics", build_file_generation = "on", diff --git a/go.mod b/go.mod index 321031d150..661fc0c5cb 100644 --- a/go.mod +++ b/go.mod @@ -104,13 +104,14 @@ require ( github.com/mattn/go-isatty v0.0.19 github.com/microsoft/ApplicationInsights-Go v0.4.4 github.com/pkg/errors v0.9.1 + github.com/regclient/regclient v0.5.5 github.com/rogpeppe/go-internal v1.11.0 github.com/schollz/progressbar/v3 v3.13.1 github.com/siderolabs/talos/pkg/machinery v1.4.6 github.com/sigstore/rekor v1.2.2 github.com/sigstore/sigstore v1.7.1 github.com/spf13/afero v1.10.0 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 github.com/theupdateframework/go-tuf v0.5.2 @@ -144,9 +145,11 @@ require ( require ( github.com/Microsoft/hcsshim v0.11.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/ulikunitz/xz v0.5.11 // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect ) diff --git a/go.sum b/go.sum index e81efa4043..89b0d8f7c3 100644 --- a/go.sum +++ b/go.sum @@ -255,7 +255,7 @@ github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -294,8 +294,8 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/edgelesssys/go-azguestattestation v0.0.0-20230707101700-a683be600fcf h1:1iKB7b+i7svWC0aKXwggi+kHf0K57g8r9hN4VOpJYYg= github.com/edgelesssys/go-azguestattestation v0.0.0-20230707101700-a683be600fcf/go.mod h1:T8Rv3qrCpUJZbKq49OA9tcC1ZbRkGtDxiafsj++LYIE= github.com/edgelesssys/go-tdx-qpl v0.0.0-20230530085549-fd2878a4dead h1:Q2TI34V/NCLGQQkdc0/KmPx/7ix9YnGDUQDT+gqvDw0= @@ -842,6 +842,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/regclient/regclient v0.5.5 h1:fDh5afBCRbeSU71vcvbN0fI88D1fTfp2m39BPqki7EU= +github.com/regclient/regclient v0.5.5/go.mod h1:604ymXFhwbmWjyfGFp3uF91gfCXIcjWBxJhBg94s9cM= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -888,8 +890,8 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -923,6 +925,8 @@ github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= diff --git a/hack/go.mod b/hack/go.mod index 38019568d6..1e4f2cde62 100644 --- a/hack/go.mod +++ b/hack/go.mod @@ -46,7 +46,7 @@ require ( github.com/edgelesssys/constellation/v2 v2.6.0 github.com/hexops/gotextdiff v1.0.3 github.com/spf13/afero v1.10.0 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.8.4 go.uber.org/goleak v1.3.0 go.uber.org/zap v1.26.0 @@ -115,7 +115,7 @@ require ( github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/containerd/containerd v1.7.6 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/hack/go.sum b/hack/go.sum index 1cf77980a9..53e6f75516 100644 --- a/hack/go.sum +++ b/hack/go.sum @@ -219,8 +219,8 @@ github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDY github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -253,8 +253,8 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/edgelesssys/go-azguestattestation v0.0.0-20230707101700-a683be600fcf h1:1iKB7b+i7svWC0aKXwggi+kHf0K57g8r9hN4VOpJYYg= github.com/edgelesssys/go-azguestattestation v0.0.0-20230707101700-a683be600fcf/go.mod h1:T8Rv3qrCpUJZbKq49OA9tcC1ZbRkGtDxiafsj++LYIE= github.com/edgelesssys/go-tdx-qpl v0.0.0-20230530085549-fd2878a4dead h1:Q2TI34V/NCLGQQkdc0/KmPx/7ix9YnGDUQDT+gqvDw0= @@ -824,8 +824,8 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= diff --git a/internal/versions/hash-generator/BUILD.bazel b/internal/versions/hash-generator/BUILD.bazel index 4503eb85e0..fd2b212820 100644 --- a/internal/versions/hash-generator/BUILD.bazel +++ b/internal/versions/hash-generator/BUILD.bazel @@ -6,7 +6,14 @@ go_library( srcs = ["generate.go"], importpath = "github.com/edgelesssys/constellation/v2/internal/versions/hash-generator", visibility = ["//visibility:private"], - deps = ["@org_golang_x_tools//go/ast/astutil"], + deps = [ + "@com_github_regclient_regclient//:regclient", + "@com_github_regclient_regclient//types/ref", + "@com_github_vincent_petithory_dataurl//:dataurl", + "@io_k8s_kubernetes//cmd/kubeadm/app/apis/kubeadm", + "@io_k8s_kubernetes//cmd/kubeadm/app/images", + "@org_golang_x_tools//go/ast/astutil", + ], ) go_binary( diff --git a/internal/versions/hash-generator/generate.go b/internal/versions/hash-generator/generate.go index fcc5b27eb2..107060bd6c 100644 --- a/internal/versions/hash-generator/generate.go +++ b/internal/versions/hash-generator/generate.go @@ -4,12 +4,20 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// hash-generator updates the binary hashes and kubeadm patches in versions.go in place. +// +// This binary is usually invoked by the //bazel/ci:go_generate target, but you can run it +// manually, too. Clear a hash or a data URL in versions.go and execute +// +// bazel run //internal/versions/hash-generator -- --update=false $PWD/internal/versions/versions.go package main import ( "bytes" "context" "crypto/sha256" + "encoding/json" + "flag" "fmt" "go/ast" "go/parser" @@ -19,52 +27,113 @@ import ( "log" "net/http" "os" + "slices" "strings" + "github.com/regclient/regclient" + "github.com/regclient/regclient/types/ref" + "github.com/vincent-petithory/dataurl" "golang.org/x/tools/go/ast/astutil" + "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + "k8s.io/kubernetes/cmd/kubeadm/app/images" ) -func mustGetHash(url string) string { - // remove quotes around url - url = url[1 : len(url)-1] +const ( + defaultRegistry = "registry.k8s.io" + etcdComponent = "etcd" + defaultFilePath = "./versions.go" +) + +var supportedComponents = []string{"kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd"} + +func quote(s string) string { + return fmt.Sprintf(`"%s"`, s) +} + +func unquote(s string) string { + return strings.TrimPrefix(strings.TrimSuffix(s, `"`), `"`) +} - // Get the data - req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil) +// pinKubernetesImage takes a component and a version and returns the corresponding container image pinned by hash. +// +// The version string is a Kubernetes version tag, which is used to derive the tags of the component images. +// The image hash is obtained directly from the default registry, registry.k8s.io. +func pinKubernetesImage(comp, ver string) (string, error) { + if !slices.Contains(supportedComponents, comp) { + return "", fmt.Errorf("k8s component %q not supported: valid components: %#v", comp, supportedComponents) + } + ref := ref.Ref{Scheme: "reg", Registry: defaultRegistry, Repository: comp, Tag: ver} + if comp == etcdComponent { + cfg := &kubeadm.ClusterConfiguration{ + KubernetesVersion: ver, + ImageRepository: defaultRegistry, + } + + img := images.GetEtcdImage(cfg) + _, tag, _ := strings.Cut(img, ":") + ref.Tag = tag + } + log.Printf("Getting hash for image %#v", ref) + + rc := regclient.New() + m, err := rc.ManifestGet(context.Background(), ref) if err != nil { - panic(err) + return "", fmt.Errorf("could not obtain image manifest: %w", err) } - resp, err := http.DefaultClient.Do(req) + + return fmt.Sprintf("%s/%s:%s@%s", ref.Registry, ref.Repository, ref.Tag, m.GetDescriptor().Digest.String()), nil +} + +func generateKubeadmPatch(comp, ver string) (string, error) { + img, err := pinKubernetesImage(comp, ver) if err != nil { - panic(err) + return "", err + } + content, err := json.Marshal([]map[string]string{{ + "op": "replace", + "path": "/spec/containers/0/image", + "value": img, + }}) + if err != nil { + return "", err + } + return dataurl.New(content, "application/json").String(), nil +} + +// hashURLContent downloads a binary blob from the given URL and calculates its SHA256 hash. +// +// URLs passed to this function are expected to have upstream signatures with a .sha256 suffix. This upstream signature +// will be verified, too. +// +// nolint:noctx // This is a cli that does not benefit from passing contexts around. +func hashURLContent(url string) (string, error) { + resp, err := http.Get(url) + if err != nil { + return "", fmt.Errorf("could not fetch URL: %w", err) } defer resp.Body.Close() // Check server response if resp.StatusCode != http.StatusOK { - panic("bad status: " + resp.Status) + return "", fmt.Errorf("unexpected HTTP response code: %d", resp.StatusCode) } // Generate SHA256 hash of the file sha := sha256.New() if _, err := io.Copy(sha, resp.Body); err != nil { - panic(err) + return "", fmt.Errorf("could not calculate response body hash: %w", err) } fileHash := sha.Sum(nil) - // Get upstream hash - req, err = http.NewRequestWithContext(context.Background(), http.MethodGet, url+".sha256", http.NoBody) - if err != nil { - panic(err) - } - resp, err = http.DefaultClient.Do(req) + resp, err = http.Get(url + ".sha256") if err != nil { - panic(err) + return "", fmt.Errorf("could not fetch upstream digest: %w", err) } defer resp.Body.Close() // Check server response if resp.StatusCode != http.StatusOK { - panic("bad status: " + resp.Status) + return "", fmt.Errorf("unexpected HTTP response code for upstream digest: %d", resp.StatusCode) } // Compare hashes @@ -73,106 +142,150 @@ func mustGetHash(url string) string { // Some .sha256 files contain additional information afterwards. upstreamHash := make([]byte, 64) if _, err = resp.Body.Read(upstreamHash); err != nil { - panic(err) + return "", fmt.Errorf("could not read upstream hash: %w", err) } if string(upstreamHash) != fmt.Sprintf("%x", fileHash) { - panic("hash mismatch") + return "", fmt.Errorf("computed hash %x does not match upstream hash %s", fileHash, string(upstreamHash)) } - return fmt.Sprintf("\"sha256:%x\"", fileHash) + return fmt.Sprintf("sha256:%x", fileHash), nil } -func main() { - fmt.Println("Generating hashes...") +type updater struct { + k8sVersion string +} - const filePath = "./versions.go" +// maybeSetVersion keeps track of the ambient ClusterVersion of components. +func (u *updater) maybeSetVersion(n ast.Node) { + kv, ok := n.(*ast.KeyValueExpr) + if !ok { + return + } + key, ok := kv.Key.(*ast.Ident) + if !ok || key.Name != "ClusterVersion" { + return + } + val, ok := kv.Value.(*ast.BasicLit) + if !ok || val.Kind != token.STRING { + return + } - fset := token.NewFileSet() - file, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments) - if err != nil { - log.Fatal(err) + u.k8sVersion = val.Value[1 : len(val.Value)-1] +} + +func (u *updater) updateComponents(cursor *astutil.Cursor) bool { + n := cursor.Node() + + u.maybeSetVersion(n) + // + // Find CompositeLit of type 'components.Components' + // + comp, ok := n.(*ast.CompositeLit) + if !ok { + return true + } + selExpr, ok := comp.Type.(*ast.SelectorExpr) + if !ok { + return true + } + if selExpr.Sel.Name != "Components" { + return true + } + xIdent, ok := selExpr.X.(*ast.Ident) + if !ok { + return true + } + if xIdent.Name != "components" { + return true } - var componentListsCtr, componentCtr int + log.Printf("Iterating over components for cluster version %q", u.k8sVersion) - newFile := astutil.Apply(file, func(cursor *astutil.Cursor) bool { - n := cursor.Node() + // + // Iterate over the components + // + for _, componentElt := range comp.Elts { + component := componentElt.(*ast.CompositeLit) - // - // Find CompositeLit of type 'components.Components' - // - comp, ok := n.(*ast.CompositeLit) - if !ok { - return true - } - selExpr, ok := comp.Type.(*ast.SelectorExpr) - if !ok { - return true - } - if selExpr.Sel.Name != "Components" { - return true - } - xIdent, ok := selExpr.X.(*ast.Ident) - if !ok { - return true - } - if xIdent.Name != "components" { - return true - } - componentListsCtr++ - - // - // Iterate over the components - // - for _, componentElt := range comp.Elts { - component := componentElt.(*ast.CompositeLit) - componentCtr++ - - var url *ast.KeyValueExpr - var hash *ast.KeyValueExpr - - for _, e := range component.Elts { - kv, ok := e.(*ast.KeyValueExpr) - if !ok { - continue - } - ident, ok := kv.Key.(*ast.Ident) - if !ok { - continue - } - switch ident.Name { - case "Url": - url = kv - case "Hash": - hash = kv - } + var url, hash, installPath *ast.KeyValueExpr + + for _, e := range component.Elts { + kv, ok := e.(*ast.KeyValueExpr) + if !ok { + continue + } + ident, ok := kv.Key.(*ast.Ident) + if !ok { + continue + } + switch ident.Name { + case "Url": + url = kv + case "Hash": + hash = kv + case "InstallPath": + installPath = kv } + } - urlValue := url.Value.(*ast.BasicLit).Value - if strings.HasPrefix(urlValue, `"data:`) { - // TODO(burgerdev): support patch generation + urlValue := unquote(url.Value.(*ast.BasicLit).Value) + if urlValue == "" || strings.HasPrefix(urlValue, "data:") { + // This can't be a downloadable component, so we assume this is supposed to be a kubeadm patch. + if urlValue != "" && !*updateHash { + continue + } + // all patch InstallPaths look like `patchFilePath("$COMPONENT")` + comp := unquote(installPath.Value.(*ast.CallExpr).Args[0].(*ast.BasicLit).Value) + log.Println("Generating kubeadm patch for", comp) + dataURL, err := generateKubeadmPatch(comp, u.k8sVersion) + if err != nil { + log.Fatalf("Could not generate kubeadm patch for %q: %v", comp, err) + } + url.Value.(*ast.BasicLit).Value = quote(dataURL) + } else { + if hash.Value.(*ast.BasicLit).Value != `""` && !*updateHash { continue } - fmt.Println("Generating hash for", urlValue) - hash.Value.(*ast.BasicLit).Value = mustGetHash(urlValue) + log.Println("Generating hash for", urlValue) + h, err := hashURLContent(urlValue) + if err != nil { + log.Fatalf("Could not hash URL %q: %v", urlValue, err) + } + hash.Value.(*ast.BasicLit).Value = quote(h) } + } - return true - }, nil, - ) + return true +} + +var updateHash = flag.Bool("update", true, "update existing hashes and data URLs") + +func main() { + log.Println("Generating hashes...") + + flag.Parse() + + filePath := flag.Arg(0) + if filePath == "" { + filePath = defaultFilePath + } + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments) + if err != nil { + log.Fatalf("Could not parse file %q: %v", filePath, err) + } + + updater := &updater{} + newFile := astutil.Apply(file, updater.updateComponents, nil) var buf bytes.Buffer printConfig := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} if err = printConfig.Fprint(&buf, fset, newFile); err != nil { - log.Fatalf("error formatting file %s: %s", filePath, err) + log.Fatalf("Could not format file %q: %v", filePath, err) } if err := os.WriteFile(filePath, buf.Bytes(), 0o644); err != nil { - log.Fatalf("error writing file %s: %s", filePath, err) + log.Fatalf("Could not write file %q: %v", filePath, err) } - if componentCtr == 0 { - log.Fatalf("no components lists found") - } - - fmt.Printf("Successfully generated hashes for %d components in %d component lists.\n", componentCtr, componentListsCtr) } diff --git a/internal/versions/versions.go b/internal/versions/versions.go index 3dbdcb41bd..b2861eb581 100644 --- a/internal/versions/versions.go +++ b/internal/versions/versions.go @@ -246,6 +246,10 @@ var VersionConfigs = map[ValidK8sVersion]KubernetesVersion{ Url: "data:application/json;base64,W3sib3AiOiJyZXBsYWNlIiwicGF0aCI6Ii9zcGVjL2NvbnRhaW5lcnMvMC9pbWFnZSIsInZhbHVlIjoicmVnaXN0cnkuazhzLmlvL2t1YmUtc2NoZWR1bGVyOnYxLjI2LjExQHNoYTI1NjowNjg0ZTIzMTcyZDkyMDMxNDk3MTU4MGFiMTE1YTViNjc5YWMxZmFlMmNiOTRkODNlOTEwNWMwYjFlOTNhMWJjIn1d", InstallPath: patchFilePath("kube-scheduler"), }, + { + Url: "data:application/json;base64,W3sib3AiOiJyZXBsYWNlIiwicGF0aCI6Ii9zcGVjL2NvbnRhaW5lcnMvMC9pbWFnZSIsInZhbHVlIjoicmVnaXN0cnkuazhzLmlvL2V0Y2Q6My41LjEwLTBAc2hhMjU2OjIyZjg5MmQ3NjcyYWRjMGI5Yzg2ZGY2Nzc5MmFmZGI4YjJkYzA4ODgwZjQ5ZjY2OWVhYWE1OWM0N2Q3OTA4YzIifV0=", + InstallPath: patchFilePath("etcd"), + }, }, // CloudControllerManagerImageAWS is the CCM image used on AWS. CloudControllerManagerImageAWS: "registry.k8s.io/provider-aws/cloud-controller-manager:v1.26.6@sha256:33445ab57f48938fe989ffe311dacee0044b82f2bd23cb7f7b563275926f0ce9", // renovate:container @@ -309,6 +313,10 @@ var VersionConfigs = map[ValidK8sVersion]KubernetesVersion{ Url: "data:application/json;base64,W3sib3AiOiJyZXBsYWNlIiwicGF0aCI6Ii9zcGVjL2NvbnRhaW5lcnMvMC9pbWFnZSIsInZhbHVlIjoicmVnaXN0cnkuazhzLmlvL2t1YmUtc2NoZWR1bGVyOnYxLjI3LjhAc2hhMjU2OjYyMzdlNzEwMGNjZGJiZDVlMGU3Y2ZmNzc5NjgzMWMxODVhMzk0NzE5OTgyM2YzOTEyODNjNzlkMDBhZmYwNzAifV0=", InstallPath: patchFilePath("kube-scheduler"), }, + { + Url: "data:application/json;base64,W3sib3AiOiJyZXBsYWNlIiwicGF0aCI6Ii9zcGVjL2NvbnRhaW5lcnMvMC9pbWFnZSIsInZhbHVlIjoicmVnaXN0cnkuazhzLmlvL2V0Y2Q6My41LjEwLTBAc2hhMjU2OjIyZjg5MmQ3NjcyYWRjMGI5Yzg2ZGY2Nzc5MmFmZGI4YjJkYzA4ODgwZjQ5ZjY2OWVhYWE1OWM0N2Q3OTA4YzIifV0=", + InstallPath: patchFilePath("etcd"), + }, }, // CloudControllerManagerImageAWS is the CCM image used on AWS. CloudControllerManagerImageAWS: "registry.k8s.io/provider-aws/cloud-controller-manager:v1.27.2@sha256:42be09a2b13b4e69b42905639d6b005ebe1ca490aabefad427256abf2cc892c7", // renovate:container @@ -372,6 +380,10 @@ var VersionConfigs = map[ValidK8sVersion]KubernetesVersion{ Url: "data:application/json;base64,W3sib3AiOiJyZXBsYWNlIiwicGF0aCI6Ii9zcGVjL2NvbnRhaW5lcnMvMC9pbWFnZSIsInZhbHVlIjoicmVnaXN0cnkuazhzLmlvL2t1YmUtc2NoZWR1bGVyOnYxLjI4LjRAc2hhMjU2OjMzNWJiYTllODYxYjg4ZmE4YjdiYjkyNTBiY2Q2OWI3YTMzZjgzZGE0ZmVlOTNmOWZjMGVlZGM2ZjM0ZTI4YmEifV0=", InstallPath: patchFilePath("kube-scheduler"), }, + { + Url: "data:application/json;base64,W3sib3AiOiJyZXBsYWNlIiwicGF0aCI6Ii9zcGVjL2NvbnRhaW5lcnMvMC9pbWFnZSIsInZhbHVlIjoicmVnaXN0cnkuazhzLmlvL2V0Y2Q6My41LjEwLTBAc2hhMjU2OjIyZjg5MmQ3NjcyYWRjMGI5Yzg2ZGY2Nzc5MmFmZGI4YjJkYzA4ODgwZjQ5ZjY2OWVhYWE1OWM0N2Q3OTA4YzIifV0=", + InstallPath: patchFilePath("etcd"), + }, }, // CloudControllerManagerImageAWS is the CCM image used on AWS. CloudControllerManagerImageAWS: "registry.k8s.io/provider-aws/cloud-controller-manager:v1.28.1@sha256:79b423ac8bc52d00f932b40de11fc3047a5ed1cbec47cda23bcf8f45ef583ed1", // renovate:container diff --git a/internal/versions/versions_test.go b/internal/versions/versions_test.go index dbab7107b3..34bc998597 100644 --- a/internal/versions/versions_test.go +++ b/internal/versions/versions_test.go @@ -52,13 +52,16 @@ func TestVersionFromDockerImage(t *testing.T) { func TestKubernetesImagePatchCompatibility(t *testing.T) { // This test ensures that pinned Kubernetes images correspond to the - // supported Kubernetes versions. It prevents automatic upgrades until - // a patch generator is added to the codebase. - // TODO(burgerdev): remove after patches are generated automatically. + // supported Kubernetes versions. for v, clusterConfig := range VersionConfigs { t.Run(string(v), func(t *testing.T) { for i, component := range clusterConfig.KubernetesComponents.GetUpgradableComponents() { if !strings.HasPrefix(component.Url, "data:") { + // This test only applies to kubeadm patches. + continue + } + if strings.Contains(component.InstallPath, "/etcd") { + // The etcd version is not derived from the Kubernetes version continue } t.Run(fmt.Sprintf("%d-%s", i, path.Base(component.InstallPath)), func(t *testing.T) { diff --git a/terraform-provider-constellation/go.mod b/terraform-provider-constellation/go.mod index 7b7eeaf0d7..060f2518c5 100644 --- a/terraform-provider-constellation/go.mod +++ b/terraform-provider-constellation/go.mod @@ -226,7 +226,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/theupdateframework/go-tuf v0.5.2 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect diff --git a/terraform-provider-constellation/go.sum b/terraform-provider-constellation/go.sum index 02f7589ebf..5c9e679c13 100644 --- a/terraform-provider-constellation/go.sum +++ b/terraform-provider-constellation/go.sum @@ -206,7 +206,7 @@ github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDY github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -239,8 +239,8 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/edgelesssys/go-azguestattestation v0.0.0-20230707101700-a683be600fcf h1:1iKB7b+i7svWC0aKXwggi+kHf0K57g8r9hN4VOpJYYg= github.com/edgelesssys/go-azguestattestation v0.0.0-20230707101700-a683be600fcf/go.mod h1:T8Rv3qrCpUJZbKq49OA9tcC1ZbRkGtDxiafsj++LYIE= github.com/edgelesssys/go-tdx-qpl v0.0.0-20230530085549-fd2878a4dead h1:Q2TI34V/NCLGQQkdc0/KmPx/7ix9YnGDUQDT+gqvDw0= @@ -844,8 +844,8 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=