diff --git a/CMakeLists.txt b/CMakeLists.txt index 354e44cc8..627f7ba29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,15 @@ add_custom_target(bootstrapper ALL BYPRODUCTS bootstrapper ) +# +# upgrade-agent +# +add_custom_target(upgrade-agent ALL + DOCKER_BUILDKIT=1 docker build -o ${CMAKE_BINARY_DIR} --build-arg PROJECT_VERSION=${PROJECT_VERSION} -f Dockerfile.build --target upgrade-agent . + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + BYPRODUCTS upgrade-agent +) + # # cli # diff --git a/Dockerfile.build b/Dockerfile.build index 6c9581786..be4bbb37a 100644 --- a/Dockerfile.build +++ b/Dockerfile.build @@ -33,8 +33,17 @@ WORKDIR /constellation/disk-mapper/ ARG PROJECT_VERSION RUN --mount=type=cache,target=/root/.cache/go-build go build -o disk-mapper -ldflags "-s -w -buildid='' -X github.com/edgelesssys/constellation/v2/internal/constants.VersionInfo=${PROJECT_VERSION}" ./cmd/ +FROM build AS build-upgrade-agent +WORKDIR /constellation/upgrade-agent/ + +ARG PROJECT_VERSION +RUN --mount=type=cache,target=/root/.cache/go-build go build -o upgrade-agent -ldflags "-s -w -buildid='' -X github.com/edgelesssys/constellation/v2/internal/constants.VersionInfo=${PROJECT_VERSION}" ./cmd/ + FROM scratch AS bootstrapper COPY --from=build-bootstrapper /constellation/bootstrapper/bootstrapper / FROM scratch AS disk-mapper COPY --from=build-disk-mapper /constellation/disk-mapper/disk-mapper / + +FROM scratch AS upgrade-agent +COPY --from=build-upgrade-agent /constellation/upgrade-agent/upgrade-agent / diff --git a/bootstrapper/internal/kubernetes/k8sapi/k8sutil.go b/bootstrapper/internal/kubernetes/k8sapi/k8sutil.go index 5be769fc3..10c2e47af 100644 --- a/bootstrapper/internal/kubernetes/k8sapi/k8sutil.go +++ b/bootstrapper/internal/kubernetes/k8sapi/k8sutil.go @@ -31,6 +31,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/crypto" "github.com/edgelesssys/constellation/v2/internal/file" + "github.com/edgelesssys/constellation/v2/internal/installer" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/versions" "github.com/spf13/afero" @@ -40,9 +41,7 @@ import ( const ( // kubeletStartTimeout is the maximum time given to the kubelet service to (re)start. kubeletStartTimeout = 10 * time.Minute - // crdTimeout is the maximum time given to the CRDs to be created. - crdTimeout = 30 * time.Second - executablePerm = 0o544 + kubeletServicePath = "/usr/lib/systemd/system/kubelet.service" ) @@ -56,7 +55,7 @@ type Client interface { AnnotateNode(ctx context.Context, nodeName, annotationKey, annotationValue string) error } -type installer interface { +type componentsInstaller interface { Install( ctx context.Context, kubernetesComponent versions.ComponentVersion, ) error @@ -64,14 +63,14 @@ type installer interface { // KubernetesUtil provides low level management of the kubernetes cluster. type KubernetesUtil struct { - inst installer + inst componentsInstaller file file.Handler } // NewKubernetesUtil creates a new KubernetesUtil. func NewKubernetesUtil() *KubernetesUtil { return &KubernetesUtil{ - inst: newOSInstaller(), + inst: installer.NewOSInstaller(), file: file.NewHandler(afero.NewOsFs()), } } diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 08c29f3f2..8a4ea7c45 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -81,7 +81,8 @@ const ( ControlPlaneAdminConfFilename = "/etc/kubernetes/admin.conf" // KubectlPath path to kubectl binary. KubectlPath = "/run/state/bin/kubectl" - + // UpgradeAgentSocketPath is the path to the UDS that is used for the gRPC connection to the upgrade agent. + UpgradeAgentSocketPath = "/run/constellation-upgrade-agent.sock" // CniPluginsDir path directory for CNI plugins. CniPluginsDir = "/opt/cni/bin" // BinDir install path for CNI config. diff --git a/bootstrapper/internal/kubernetes/k8sapi/install.go b/internal/installer/install.go similarity index 93% rename from bootstrapper/internal/kubernetes/k8sapi/install.go rename to internal/installer/install.go index 7d9dd95df..cfcffa04d 100644 --- a/bootstrapper/internal/kubernetes/k8sapi/install.go +++ b/internal/installer/install.go @@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package k8sapi +package installer import ( "archive/tar" @@ -29,10 +29,11 @@ import ( const ( // determines the period after which retryDownloadToTempDir will retry a download. downloadInterval = 10 * time.Millisecond + executablePerm = 0o544 ) -// osInstaller installs binary components of supported kubernetes versions. -type osInstaller struct { +// OsInstaller installs binary components of supported kubernetes versions. +type OsInstaller struct { fs *afero.Afero hClient httpClient // clock is needed for testing purposes @@ -41,9 +42,9 @@ type osInstaller struct { retriable func(error) bool } -// newOSInstaller creates a new osInstaller. -func newOSInstaller() *osInstaller { - return &osInstaller{ +// NewOSInstaller creates a new osInstaller. +func NewOSInstaller() *OsInstaller { + return &OsInstaller{ fs: &afero.Afero{Fs: afero.NewOsFs()}, hClient: &http.Client{}, clock: clock.RealClock{}, @@ -53,7 +54,7 @@ func newOSInstaller() *osInstaller { // Install downloads a resource from a URL, applies any given text transformations and extracts the resulting file if required. // The resulting file(s) are copied to the destination. It also verifies the sha256 hash of the downloaded file. -func (i *osInstaller) Install(ctx context.Context, kubernetesComponent versions.ComponentVersion) error { +func (i *OsInstaller) Install(ctx context.Context, kubernetesComponent versions.ComponentVersion) error { tempPath, err := i.retryDownloadToTempDir(ctx, kubernetesComponent.URL) if err != nil { return err @@ -88,7 +89,7 @@ func (i *osInstaller) Install(ctx context.Context, kubernetesComponent versions. } // extractArchive extracts tar gz archives to a prefixed destination. -func (i *osInstaller) extractArchive(archivePath, prefix string, perm fs.FileMode) error { +func (i *OsInstaller) extractArchive(archivePath, prefix string, perm fs.FileMode) error { archiveFile, err := i.fs.Open(archivePath) if err != nil { return fmt.Errorf("opening archive file: %w", err) @@ -160,7 +161,7 @@ func (i *osInstaller) extractArchive(archivePath, prefix string, perm fs.FileMod } } -func (i *osInstaller) retryDownloadToTempDir(ctx context.Context, url string) (fileName string, someError error) { +func (i *OsInstaller) retryDownloadToTempDir(ctx context.Context, url string) (fileName string, someError error) { doer := downloadDoer{ url: url, downloader: i, @@ -177,7 +178,7 @@ func (i *osInstaller) retryDownloadToTempDir(ctx context.Context, url string) (f } // downloadToTempDir downloads a file to a temporary location. -func (i *osInstaller) downloadToTempDir(ctx context.Context, url string) (fileName string, retErr error) { +func (i *OsInstaller) downloadToTempDir(ctx context.Context, url string) (fileName string, retErr error) { out, err := afero.TempFile(i.fs, "", "") if err != nil { return "", fmt.Errorf("creating destination temp file: %w", err) @@ -211,7 +212,7 @@ func (i *osInstaller) downloadToTempDir(ctx context.Context, url string) (fileNa } // copy copies a file from oldname to newname. -func (i *osInstaller) copy(oldname, newname string, perm fs.FileMode) (err error) { +func (i *OsInstaller) copy(oldname, newname string, perm fs.FileMode) (err error) { old, openOldErr := i.fs.OpenFile(oldname, os.O_RDONLY, fs.ModePerm) if openOldErr != nil { return fmt.Errorf("copying %q to %q: cannot open source file for reading: %w", oldname, newname, openOldErr) diff --git a/bootstrapper/internal/kubernetes/k8sapi/install_test.go b/internal/installer/install_test.go similarity index 99% rename from bootstrapper/internal/kubernetes/k8sapi/install_test.go rename to internal/installer/install_test.go index 1c9809c09..f81ff93ba 100644 --- a/bootstrapper/internal/kubernetes/k8sapi/install_test.go +++ b/internal/installer/install_test.go @@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package k8sapi +package installer import ( "archive/tar" @@ -98,7 +98,7 @@ func TestInstall(t *testing.T) { } // This test was written before retriability was added to Install. It makes sense to test Install as if it wouldn't retry requests. - inst := osInstaller{ + inst := OsInstaller{ fs: &afero.Afero{Fs: afero.NewMemMapFs()}, hClient: &hClient, clock: testclock.NewFakeClock(time.Time{}), @@ -245,7 +245,7 @@ func TestExtractArchive(t *testing.T) { afs = afero.NewReadOnlyFs(afs) } - inst := osInstaller{ + inst := OsInstaller{ fs: &afero.Afero{Fs: afs}, } err := inst.extractArchive(tc.source, tc.destination, fs.ModePerm) @@ -305,7 +305,7 @@ func TestRetryDownloadToTempDir(t *testing.T) { // control download retries through FakeClock clock clock := testclock.NewFakeClock(time.Now()) - inst := osInstaller{ + inst := OsInstaller{ fs: &afero.Afero{Fs: afs}, hClient: &hClient, clock: clock, @@ -398,7 +398,7 @@ func TestDownloadToTempDir(t *testing.T) { if tc.readonly { afs = afero.NewReadOnlyFs(afs) } - inst := osInstaller{ + inst := OsInstaller{ fs: &afero.Afero{Fs: afs}, hClient: &hClient, } @@ -455,7 +455,7 @@ func TestCopy(t *testing.T) { afs = afero.NewReadOnlyFs(afs) } - inst := osInstaller{fs: &afero.Afero{Fs: afs}} + inst := OsInstaller{fs: &afero.Afero{Fs: afs}} err := inst.copy(tc.oldname, tc.newname, tc.perm) if tc.wantErr { assert.Error(err) diff --git a/proto/Dockerfile.gen-proto b/proto/Dockerfile.gen-proto index 78deb6ac9..c36c06d2c 100644 --- a/proto/Dockerfile.gen-proto +++ b/proto/Dockerfile.gen-proto @@ -54,6 +54,11 @@ WORKDIR /init COPY bootstrapper/initproto/*.proto /init RUN protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto +## upgrade agent +WORKDIR /upgrade-agent +COPY upgrade-agent/upgradeproto/*.proto /upgrade-agent +RUN protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto + FROM scratch as export COPY --from=build /disk-mapper/*.go disk-mapper/recoverproto/ COPY --from=build /service/*.go debugd/service/ @@ -61,3 +66,4 @@ COPY --from=build /kms/*.go kms/kmsproto/ COPY --from=build /joinservice/*.go joinservice/joinproto/ COPY --from=build /verify/*.go verify/verifyproto/ COPY --from=build /init/*.go bootstrapper/initproto/ +COPY --from=build /upgrade-agent/*.go upgrade-agent/upgradeproto/ diff --git a/upgrade-agent/cmd/main.go b/upgrade-agent/cmd/main.go new file mode 100644 index 000000000..e2d49ea19 --- /dev/null +++ b/upgrade-agent/cmd/main.go @@ -0,0 +1,48 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package main + +import ( + "flag" + + "github.com/edgelesssys/constellation/v2/internal/constants" + "github.com/edgelesssys/constellation/v2/internal/file" + "github.com/edgelesssys/constellation/v2/internal/logger" + upgradeagent "github.com/edgelesssys/constellation/v2/upgrade-agent" + "github.com/spf13/afero" + "go.uber.org/zap" +) + +const ( + protocol = "unix" +) + +func main() { + gRPCDebug := flag.Bool("debug", false, "Enable gRPC debug logging") + verbosity := flag.Int("v", 0, logger.CmdLineVerbosityDescription) + flag.Parse() + + log := logger.New(logger.JSONLog, logger.VerbosityFromInt(*verbosity)).Named("bootstrapper") + defer log.Sync() + + if *gRPCDebug { + log.Named("gRPC").ReplaceGRPCLogger() + } else { + log.Named("gRPC").WithIncreasedLevel(zap.WarnLevel).ReplaceGRPCLogger() + } + + handler := file.NewHandler(afero.NewOsFs()) + server, err := upgradeagent.New(log, handler) + if err != nil { + log.With(zap.Error(err)).Fatalf("Failed to create update server") + } + + err = server.Run(protocol, constants.UpgradeAgentSocketPath) + if err != nil { + log.With(zap.Error(err)).Fatalf("Failed to start update server") + } +} diff --git a/upgrade-agent/server.go b/upgrade-agent/server.go new file mode 100644 index 000000000..276d3b2ab --- /dev/null +++ b/upgrade-agent/server.go @@ -0,0 +1,161 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package upgradeagent + +import ( + "context" + "errors" + "fmt" + "io/fs" + "net" + "os" + "os/exec" + + "github.com/edgelesssys/constellation/v2/internal/constants" + "github.com/edgelesssys/constellation/v2/internal/file" + "github.com/edgelesssys/constellation/v2/internal/installer" + "github.com/edgelesssys/constellation/v2/internal/logger" + "github.com/edgelesssys/constellation/v2/internal/versions" + "github.com/edgelesssys/constellation/v2/upgrade-agent/upgradeproto" + "golang.org/x/mod/semver" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var errInvalidKubernetesVersion = errors.New("invalid kubernetes version") + +// Server is the upgrade-agent server. +type Server struct { + file file.Handler + grpcServer serveStopper + log *logger.Logger + upgradeproto.UnimplementedUpdateServer +} + +// New creates a new upgrade-agent server. +func New(log *logger.Logger, fileHandler file.Handler) (*Server, error) { + log = log.Named("upgradeServer") + + server := &Server{ + log: log, + file: fileHandler, + } + + grpcServer := grpc.NewServer( + log.Named("gRPC").GetServerUnaryInterceptor(), + ) + upgradeproto.RegisterUpdateServer(grpcServer, server) + + server.grpcServer = grpcServer + return server, nil +} + +// Run starts the upgrade-agent server on the given port, using the provided protocol and socket address. +func (s *Server) Run(protocol string, sockAddr string) error { + grpcServer := grpc.NewServer() + + upgradeproto.RegisterUpdateServer(grpcServer, s) + + cleanup := func() error { + err := os.RemoveAll(sockAddr) + if errors.Is(err, fs.ErrNotExist) { + return nil + } else if err != nil { + return err + } + return nil + } + if err := cleanup(); err != nil { + return fmt.Errorf("failed to clean socket file: %s", err) + } + + lis, err := net.Listen(protocol, sockAddr) + if err != nil { + return fmt.Errorf("failed to listen: %s", err) + } + + s.log.Infof("Starting") + return grpcServer.Serve(lis) +} + +// Stop stops the upgrade-agent server gracefully. +func (s *Server) Stop() { + s.log.Infof("Stopping") + + s.grpcServer.GracefulStop() + + s.log.Infof("Stopped") +} + +// ExecuteUpdate installs & verifies the provided kubeadm, then executes `kubeadm upgrade plan` & `kubeadm upgrade apply {wanted_Kubernetes_Version}` to upgrade to the specified version. +func (s *Server) ExecuteUpdate(ctx context.Context, updateRequest *upgradeproto.ExecuteUpdateRequest) (*upgradeproto.ExecuteUpdateResponse, error) { + s.log.Infof("Upgrade to Kubernetes version started: %s", updateRequest.WantedKubernetesVersion) + + installer := installer.NewOSInstaller() + err := prepareUpdate(ctx, installer, updateRequest) + if errors.Is(err, errInvalidKubernetesVersion) { + return &upgradeproto.ExecuteUpdateResponse{}, status.Errorf(codes.Internal, "unable to verify the Kubernetes version %s: %s", updateRequest.WantedKubernetesVersion, err) + } else if err != nil { + return &upgradeproto.ExecuteUpdateResponse{}, status.Errorf(codes.Internal, "unable to install the kubeadm binary: %s", err) + } + + upgradeCmd := exec.CommandContext(ctx, "kubeadm", "upgrade", "plan") + if err := upgradeCmd.Run(); err != nil { + return &upgradeproto.ExecuteUpdateResponse{}, status.Errorf(codes.Internal, "unable to execute kubeadm upgrade plan: %s", err) + } + + applyCmd := exec.CommandContext(ctx, "kubeadm", "upgrade", "apply", updateRequest.WantedKubernetesVersion) + if err := applyCmd.Run(); err != nil { + return &upgradeproto.ExecuteUpdateResponse{}, status.Errorf(codes.Internal, "unable to execute kubeadm upgrade apply: %s", err) + } + + s.log.Infof("Upgrade to Kubernetes version succeeded: %s", updateRequest.WantedKubernetesVersion) + return &upgradeproto.ExecuteUpdateResponse{}, nil +} + +// prepareUpdate downloads & installs the specified kubeadm version and verifies the desired Kubernetes version. +func prepareUpdate(ctx context.Context, installer osInstaller, updateRequest *upgradeproto.ExecuteUpdateRequest) error { + // verify Kubernetes version + err := verifyVersion(updateRequest.WantedKubernetesVersion) + if err != nil { + return err + } + + // download & install the kubeadm binary + err = installer.Install(ctx, versions.ComponentVersion{ + URL: updateRequest.KubeadmUrl, + Hash: updateRequest.KubeadmHash, + InstallPath: constants.KubeadmPath, + Extract: false, + }) + if err != nil { + return err + } + + return nil +} + +// verifyVersion verifies the provided Kubernetes version. +func verifyVersion(version string) error { + if !semver.IsValid(version) { + return errInvalidKubernetesVersion + } + return nil +} + +type osInstaller interface { + // Install downloads, installs and verifies the kubernetes component. + Install(ctx context.Context, kubernetesComponent versions.ComponentVersion) error +} + +type serveStopper interface { + // Serve starts the server. + Serve(lis net.Listener) error + // GracefulStop stops the server and blocks until all requests are done. + GracefulStop() +} diff --git a/upgrade-agent/server_test.go b/upgrade-agent/server_test.go new file mode 100644 index 000000000..3878d3a1d --- /dev/null +++ b/upgrade-agent/server_test.go @@ -0,0 +1,105 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package upgradeagent + +import ( + "context" + "fmt" + "testing" + + "github.com/edgelesssys/constellation/v2/internal/versions" + "github.com/edgelesssys/constellation/v2/upgrade-agent/upgradeproto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestVersionVerifier(t *testing.T) { + testCases := map[string]struct { + versionString string + wantErr bool + }{ + "valid version": { + versionString: "v1.1.1", + }, + "v prefix missing": { + versionString: "1.1.1", + wantErr: true, + }, + "invalid space": { + versionString: "v 1.1.1", + wantErr: true, + }, + "invalid version": { + versionString: "v1.1.1a", + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + require := require.New(t) + assert := assert.New(t) + + err := verifyVersion(tc.versionString) + if tc.wantErr { + assert.Error(err) + return + } + + require.NoError(err) + }) + } +} + +func TestPrepareUpdate(t *testing.T) { + validUpdateRequest := &upgradeproto.ExecuteUpdateRequest{ + WantedKubernetesVersion: "v1.1.1", + } + testCases := map[string]struct { + installer osInstaller + updateRequest *upgradeproto.ExecuteUpdateRequest + wantErr bool + }{ + "works": { + installer: stubOsInstaller{}, + updateRequest: validUpdateRequest, + }, + "invalid version string": { + installer: stubOsInstaller{}, + updateRequest: &upgradeproto.ExecuteUpdateRequest{WantedKubernetesVersion: "1337"}, + wantErr: true, + }, + "install error": { + installer: stubOsInstaller{InstallErr: fmt.Errorf("install error")}, + updateRequest: validUpdateRequest, + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + require := require.New(t) + assert := assert.New(t) + + err := prepareUpdate(context.Background(), tc.installer, tc.updateRequest) + if tc.wantErr { + assert.Error(err) + return + } + + require.NoError(err) + }) + } +} + +type stubOsInstaller struct { + InstallErr error +} + +func (s stubOsInstaller) Install(ctx context.Context, kubernetesComponent versions.ComponentVersion) error { + return s.InstallErr +} diff --git a/upgrade-agent/upgradeproto/upgrade.pb.go b/upgrade-agent/upgradeproto/upgrade.pb.go new file mode 100644 index 000000000..7c9ecaab2 --- /dev/null +++ b/upgrade-agent/upgradeproto/upgrade.pb.go @@ -0,0 +1,229 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.8 +// source: upgrade.proto + +package upgradeproto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ExecuteUpdateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + KubeadmUrl string `protobuf:"bytes,1,opt,name=kubeadm_url,json=kubeadmUrl,proto3" json:"kubeadm_url,omitempty"` + KubeadmHash string `protobuf:"bytes,2,opt,name=kubeadm_hash,json=kubeadmHash,proto3" json:"kubeadm_hash,omitempty"` + WantedKubernetesVersion string `protobuf:"bytes,3,opt,name=wanted_kubernetes_version,json=wantedKubernetesVersion,proto3" json:"wanted_kubernetes_version,omitempty"` +} + +func (x *ExecuteUpdateRequest) Reset() { + *x = ExecuteUpdateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_upgrade_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecuteUpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteUpdateRequest) ProtoMessage() {} + +func (x *ExecuteUpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_upgrade_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteUpdateRequest.ProtoReflect.Descriptor instead. +func (*ExecuteUpdateRequest) Descriptor() ([]byte, []int) { + return file_upgrade_proto_rawDescGZIP(), []int{0} +} + +func (x *ExecuteUpdateRequest) GetKubeadmUrl() string { + if x != nil { + return x.KubeadmUrl + } + return "" +} + +func (x *ExecuteUpdateRequest) GetKubeadmHash() string { + if x != nil { + return x.KubeadmHash + } + return "" +} + +func (x *ExecuteUpdateRequest) GetWantedKubernetesVersion() string { + if x != nil { + return x.WantedKubernetesVersion + } + return "" +} + +type ExecuteUpdateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ExecuteUpdateResponse) Reset() { + *x = ExecuteUpdateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_upgrade_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecuteUpdateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteUpdateResponse) ProtoMessage() {} + +func (x *ExecuteUpdateResponse) ProtoReflect() protoreflect.Message { + mi := &file_upgrade_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteUpdateResponse.ProtoReflect.Descriptor instead. +func (*ExecuteUpdateResponse) Descriptor() ([]byte, []int) { + return file_upgrade_proto_rawDescGZIP(), []int{1} +} + +var File_upgrade_proto protoreflect.FileDescriptor + +var file_upgrade_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x14, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x61, 0x64, 0x6d, 0x5f, 0x75, 0x72, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x61, 0x64, 0x6d, 0x55, + 0x72, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x6b, 0x75, 0x62, 0x65, 0x61, 0x64, 0x6d, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x61, 0x64, + 0x6d, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3a, 0x0a, 0x19, 0x77, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x5f, + 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x77, 0x61, 0x6e, 0x74, 0x65, 0x64, + 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x22, 0x17, 0x0a, 0x15, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x58, 0x0a, 0x06, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0d, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x2e, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x2e, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x44, 0x5a, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, + 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x75, 0x70, + 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_upgrade_proto_rawDescOnce sync.Once + file_upgrade_proto_rawDescData = file_upgrade_proto_rawDesc +) + +func file_upgrade_proto_rawDescGZIP() []byte { + file_upgrade_proto_rawDescOnce.Do(func() { + file_upgrade_proto_rawDescData = protoimpl.X.CompressGZIP(file_upgrade_proto_rawDescData) + }) + return file_upgrade_proto_rawDescData +} + +var file_upgrade_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_upgrade_proto_goTypes = []interface{}{ + (*ExecuteUpdateRequest)(nil), // 0: upgrade.ExecuteUpdateRequest + (*ExecuteUpdateResponse)(nil), // 1: upgrade.ExecuteUpdateResponse +} +var file_upgrade_proto_depIdxs = []int32{ + 0, // 0: upgrade.Update.ExecuteUpdate:input_type -> upgrade.ExecuteUpdateRequest + 1, // 1: upgrade.Update.ExecuteUpdate:output_type -> upgrade.ExecuteUpdateResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_upgrade_proto_init() } +func file_upgrade_proto_init() { + if File_upgrade_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_upgrade_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecuteUpdateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_upgrade_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecuteUpdateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_upgrade_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_upgrade_proto_goTypes, + DependencyIndexes: file_upgrade_proto_depIdxs, + MessageInfos: file_upgrade_proto_msgTypes, + }.Build() + File_upgrade_proto = out.File + file_upgrade_proto_rawDesc = nil + file_upgrade_proto_goTypes = nil + file_upgrade_proto_depIdxs = nil +} diff --git a/upgrade-agent/upgradeproto/upgrade.proto b/upgrade-agent/upgradeproto/upgrade.proto new file mode 100644 index 000000000..e07db1a75 --- /dev/null +++ b/upgrade-agent/upgradeproto/upgrade.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package upgrade; + +option go_package = "github.com/edgelesssys/constellation/v2/upgrade-agent/upgradeproto"; + +service Update { + rpc ExecuteUpdate(ExecuteUpdateRequest) returns (ExecuteUpdateResponse); +} + +message ExecuteUpdateRequest { + string kubeadm_url = 1; + string kubeadm_hash = 2; + string wanted_kubernetes_version = 3; +} + +message ExecuteUpdateResponse { +} diff --git a/upgrade-agent/upgradeproto/upgrade_grpc.pb.go b/upgrade-agent/upgradeproto/upgrade_grpc.pb.go new file mode 100644 index 000000000..5727907e5 --- /dev/null +++ b/upgrade-agent/upgradeproto/upgrade_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.8 +// source: upgrade.proto + +package upgradeproto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// UpdateClient is the client API for Update service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type UpdateClient interface { + ExecuteUpdate(ctx context.Context, in *ExecuteUpdateRequest, opts ...grpc.CallOption) (*ExecuteUpdateResponse, error) +} + +type updateClient struct { + cc grpc.ClientConnInterface +} + +func NewUpdateClient(cc grpc.ClientConnInterface) UpdateClient { + return &updateClient{cc} +} + +func (c *updateClient) ExecuteUpdate(ctx context.Context, in *ExecuteUpdateRequest, opts ...grpc.CallOption) (*ExecuteUpdateResponse, error) { + out := new(ExecuteUpdateResponse) + err := c.cc.Invoke(ctx, "/upgrade.Update/ExecuteUpdate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UpdateServer is the server API for Update service. +// All implementations must embed UnimplementedUpdateServer +// for forward compatibility +type UpdateServer interface { + ExecuteUpdate(context.Context, *ExecuteUpdateRequest) (*ExecuteUpdateResponse, error) + mustEmbedUnimplementedUpdateServer() +} + +// UnimplementedUpdateServer must be embedded to have forward compatible implementations. +type UnimplementedUpdateServer struct { +} + +func (UnimplementedUpdateServer) ExecuteUpdate(context.Context, *ExecuteUpdateRequest) (*ExecuteUpdateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExecuteUpdate not implemented") +} +func (UnimplementedUpdateServer) mustEmbedUnimplementedUpdateServer() {} + +// UnsafeUpdateServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to UpdateServer will +// result in compilation errors. +type UnsafeUpdateServer interface { + mustEmbedUnimplementedUpdateServer() +} + +func RegisterUpdateServer(s grpc.ServiceRegistrar, srv UpdateServer) { + s.RegisterService(&Update_ServiceDesc, srv) +} + +func _Update_ExecuteUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExecuteUpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UpdateServer).ExecuteUpdate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/upgrade.Update/ExecuteUpdate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UpdateServer).ExecuteUpdate(ctx, req.(*ExecuteUpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Update_ServiceDesc is the grpc.ServiceDesc for Update service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Update_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "upgrade.Update", + HandlerType: (*UpdateServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ExecuteUpdate", + Handler: _Update_ExecuteUpdate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "upgrade.proto", +}