From 66a1ba00d41234dc2928746a0eb4024bf8efda33 Mon Sep 17 00:00:00 2001 From: iQQBot Date: Tue, 12 Nov 2024 21:25:51 +0800 Subject: [PATCH] Get more insights on workspace image (#20356) * 1 * 1 * 1 * add timeout ctx * addressed feedback --- .../registry-facade/pkg/registry/manifest.go | 11 ++ components/ws-daemon/go.mod | 3 +- components/ws-daemon/go.sum | 4 + .../ws-daemon/pkg/container/container.go | 4 + .../ws-daemon/pkg/container/containerd.go | 40 +++++ components/ws-daemon/pkg/controller/mock.go | 11 +- .../ws-daemon/pkg/controller/suite_test.go | 2 +- .../pkg/controller/workspace_controller.go | 39 ++++- .../pkg/controller/workspace_operations.go | 33 +++- components/ws-daemon/pkg/daemon/daemon.go | 2 +- .../go/crd/v1/workspace_types.go | 14 ++ .../go/crd/v1/zz_generated.deepcopy.go | 21 ++- components/ws-manager-mk2/Makefile | 2 +- .../bases/workspace.gitpod.io_snapshots.yaml | 20 ++- .../bases/workspace.gitpod.io_workspaces.yaml | 165 +++++++++--------- .../config/webhook/manifests.yaml | 2 - install/installer/go.mod | 3 +- install/installer/go.sum | 5 +- 18 files changed, 274 insertions(+), 107 deletions(-) diff --git a/components/registry-facade/pkg/registry/manifest.go b/components/registry-facade/pkg/registry/manifest.go index dd18d25c52645a..979f9cdfb34cb8 100644 --- a/components/registry-facade/pkg/registry/manifest.go +++ b/components/registry-facade/pkg/registry/manifest.go @@ -12,6 +12,7 @@ import ( "io" "mime" "net/http" + "strconv" "strings" "time" @@ -185,6 +186,11 @@ func (mh *manifestHandler) getManifest(w http.ResponseWriter, r *http.Request) { return err } + originImageSize := 0 + for _, layer := range manifest.Layers { + originImageSize += int(layer.Size) + } + // modify config addonLayer, err := mh.ConfigModifier(ctx, mh.Spec, cfg) if err != nil { @@ -192,6 +198,11 @@ func (mh *manifestHandler) getManifest(w http.ResponseWriter, r *http.Request) { return err } manifest.Layers = append(manifest.Layers, addonLayer...) + if manifest.Annotations == nil { + manifest.Annotations = make(map[string]string) + } + manifest.Annotations["io.gitpod.workspace-image.size"] = strconv.Itoa(originImageSize) + manifest.Annotations["io.gitpod.workspace-image.ref"] = mh.Spec.BaseRef // place config in store rawCfg, err := json.Marshal(cfg) diff --git a/components/ws-daemon/go.mod b/components/ws-daemon/go.mod index ec82d39f55f17b..84094a360b0e39 100644 --- a/components/ws-daemon/go.mod +++ b/components/ws-daemon/go.mod @@ -82,6 +82,7 @@ require ( github.com/containerd/continuity v0.4.2 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/containerd/ttrpc v1.2.2 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect @@ -144,7 +145,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/selinux v1.11.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/components/ws-daemon/go.sum b/components/ws-daemon/go.sum index 19363de14d50da..be1876f447c176 100644 --- a/components/ws-daemon/go.sum +++ b/components/ws-daemon/go.sum @@ -1020,6 +1020,8 @@ github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3 github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.11.3/go.mod h1:7vRJIcImfY8bpifnMjt+HTJoQxASq7T28MYbP15/Nf0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= @@ -1668,6 +1670,8 @@ github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= diff --git a/components/ws-daemon/pkg/container/container.go b/components/ws-daemon/pkg/container/container.go index c3445ffae6ca47..93c71685a482ec 100644 --- a/components/ws-daemon/pkg/container/container.go +++ b/components/ws-daemon/pkg/container/container.go @@ -8,6 +8,8 @@ import ( "context" "golang.org/x/xerrors" + + workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1" ) // Runtime abstracts over the different container runtimes out there w.r.t. to the features we need from those runtimes @@ -47,6 +49,8 @@ type Runtime interface { // IsContainerdReady returns is the status of containerd. IsContainerdReady(ctx context.Context) (bool, error) + + GetContainerImageInfo(ctx context.Context, id ID) (*workspacev1.WorkspaceImageInfo, error) } var ( diff --git a/components/ws-daemon/pkg/container/containerd.go b/components/ws-daemon/pkg/container/containerd.go index a156068d86c0ba..8c2de20be48219 100644 --- a/components/ws-daemon/pkg/container/containerd.go +++ b/components/ws-daemon/pkg/container/containerd.go @@ -10,6 +10,7 @@ import ( "fmt" "path/filepath" "regexp" + "strconv" "strings" "sync" "time" @@ -20,6 +21,8 @@ import ( "github.com/containerd/containerd/api/types" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" + "github.com/containerd/platforms" "github.com/containerd/typeurl/v2" ocispecs "github.com/opencontainers/runtime-spec/specs-go" "github.com/opentracing/opentracing-go" @@ -28,6 +31,7 @@ import ( wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes" "github.com/gitpod-io/gitpod/common-go/log" "github.com/gitpod-io/gitpod/common-go/tracing" + workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1" ) const ( @@ -92,6 +96,7 @@ type containerInfo struct { UpperDir string CGroupPath string PID uint32 + ImageRef string } // start listening to containerd @@ -276,6 +281,7 @@ func (s *Containerd) handleNewContainer(c containers.Container) { info.ID = c.ID info.SnapshotKey = c.SnapshotKey info.Snapshotter = c.Snapshotter + info.ImageRef = c.Image s.cntIdx[c.ID] = info log.WithField("podname", podName).WithFields(log.OWI(info.OwnerID, info.WorkspaceID, info.InstanceID)).WithField("ID", c.ID).Debug("found workspace container - updating label cache") @@ -480,6 +486,40 @@ func (s *Containerd) ContainerPID(ctx context.Context, id ID) (pid uint64, err e return uint64(info.PID), nil } +func (s *Containerd) GetContainerImageInfo(ctx context.Context, id ID) (*workspacev1.WorkspaceImageInfo, error) { + info, ok := s.cntIdx[string(id)] + if !ok { + return nil, ErrNotFound + } + + image, err := s.Client.GetImage(ctx, info.ImageRef) + if err != nil { + return nil, err + } + size, err := image.Size(ctx) + if err != nil { + return nil, err + } + + wsImageInfo := &workspacev1.WorkspaceImageInfo{ + TotalSize: size, + } + + // Fetch the manifest + manifest, err := images.Manifest(ctx, s.Client.ContentStore(), image.Target(), platforms.Default()) + if err != nil { + log.WithError(err).WithField("image", info.ImageRef).Error("Failed to get manifest") + return wsImageInfo, nil + } + if manifest.Annotations != nil { + wsImageInfo.WorkspaceImageRef = manifest.Annotations["io.gitpod.workspace-image.ref"] + if size, err := strconv.Atoi(manifest.Annotations["io.gitpod.workspace-image.size"]); err == nil { + wsImageInfo.WorkspaceImageSize = int64(size) + } + } + return wsImageInfo, nil +} + func (s *Containerd) IsContainerdReady(ctx context.Context) (bool, error) { if len(s.registryFacadeHost) == 0 { return s.Client.IsServing(ctx) diff --git a/components/ws-daemon/pkg/controller/mock.go b/components/ws-daemon/pkg/controller/mock.go index 7103996b8b73b2..94083127f7ca6b 100644 --- a/components/ws-daemon/pkg/controller/mock.go +++ b/components/ws-daemon/pkg/controller/mock.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Gitpod GmbH. All rights reserved. +// Copyright (c) 2024 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License.AGPL.txt in the project root for license information. @@ -13,6 +13,7 @@ import ( reflect "reflect" api "github.com/gitpod-io/gitpod/content-service/api" + v1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1" gomock "github.com/golang/mock/gomock" ) @@ -84,17 +85,17 @@ func (mr *MockWorkspaceOperationsMockRecorder) InitWorkspace(arg0, arg1 interfac } // SetupWorkspace mocks base method. -func (m *MockWorkspaceOperations) SetupWorkspace(arg0 context.Context, arg1 string) error { +func (m *MockWorkspaceOperations) SetupWorkspace(arg0 context.Context, arg1 string, arg2 *v1.WorkspaceImageInfo) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetupWorkspace", arg0, arg1) + ret := m.ctrl.Call(m, "SetupWorkspace", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // SetupWorkspace indicates an expected call of SetupWorkspace. -func (mr *MockWorkspaceOperationsMockRecorder) SetupWorkspace(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockWorkspaceOperationsMockRecorder) SetupWorkspace(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupWorkspace", reflect.TypeOf((*MockWorkspaceOperations)(nil).SetupWorkspace), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupWorkspace", reflect.TypeOf((*MockWorkspaceOperations)(nil).SetupWorkspace), arg0, arg1, arg2) } // Snapshot mocks base method. diff --git a/components/ws-daemon/pkg/controller/suite_test.go b/components/ws-daemon/pkg/controller/suite_test.go index ddb699208c2ec4..be1e1ccb5e0773 100644 --- a/components/ws-daemon/pkg/controller/suite_test.go +++ b/components/ws-daemon/pkg/controller/suite_test.go @@ -77,7 +77,7 @@ var _ = BeforeSuite(func() { Expect(err).ToNot(HaveOccurred()) ctx, cancel = context.WithCancel(context.Background()) - workspaceCtrl, err = NewWorkspaceController(k8sClient, record.NewFakeRecorder(100), NodeName, secretsNamespace, 5, nil, ctrl_metrics.Registry) + workspaceCtrl, err = NewWorkspaceController(k8sClient, record.NewFakeRecorder(100), NodeName, secretsNamespace, 5, nil, ctrl_metrics.Registry, nil) Expect(err).NotTo(HaveOccurred()) Expect(workspaceCtrl.SetupWithManager(k8sManager)).To(Succeed()) diff --git a/components/ws-daemon/pkg/controller/workspace_controller.go b/components/ws-daemon/pkg/controller/workspace_controller.go index ded5fffa329ac4..134ffd5bb6bbe5 100644 --- a/components/ws-daemon/pkg/controller/workspace_controller.go +++ b/components/ws-daemon/pkg/controller/workspace_controller.go @@ -61,9 +61,10 @@ type WorkspaceController struct { metrics *workspaceMetrics secretNamespace string recorder record.EventRecorder + runtime container.Runtime } -func NewWorkspaceController(c client.Client, recorder record.EventRecorder, nodeName, secretNamespace string, maxConcurrentReconciles int, ops WorkspaceOperations, reg prometheus.Registerer) (*WorkspaceController, error) { +func NewWorkspaceController(c client.Client, recorder record.EventRecorder, nodeName, secretNamespace string, maxConcurrentReconciles int, ops WorkspaceOperations, reg prometheus.Registerer, runtime container.Runtime) (*WorkspaceController, error) { metrics := newWorkspaceMetrics() reg.Register(metrics) @@ -75,6 +76,7 @@ func NewWorkspaceController(c client.Client, recorder record.EventRecorder, node metrics: metrics, secretNamespace: secretNamespace, recorder: recorder, + runtime: runtime, }, nil } @@ -219,7 +221,40 @@ func (wsc *WorkspaceController) handleWorkspaceRunning(ctx context.Context, ws * span, ctx := opentracing.StartSpanFromContext(ctx, "handleWorkspaceRunning") defer tracing.FinishSpan(span, &err) - return ctrl.Result{}, wsc.operations.SetupWorkspace(ctx, ws.Name) + var imageInfo *workspacev1.WorkspaceImageInfo = nil + if ws.Status.ImageInfo == nil { + getImageInfo := func() (*workspacev1.WorkspaceImageInfo, error) { + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + id, err := wsc.runtime.WaitForContainer(ctx, ws.Name) + if err != nil { + return nil, fmt.Errorf("failed to wait for container: %w", err) + } + info, err := wsc.runtime.GetContainerImageInfo(ctx, id) + if err != nil { + return nil, fmt.Errorf("failed to get container image info: %w", err) + } + + err = retry.RetryOnConflict(retryParams, func() error { + if err := wsc.Get(ctx, req.NamespacedName, ws); err != nil { + return err + } + ws.Status.ImageInfo = info + return wsc.Status().Update(ctx, ws) + }) + if err != nil { + return info, fmt.Errorf("failed to update workspace with image info: %w", err) + } + return info, nil + } + imageInfo, err = getImageInfo() + if err != nil { + glog.WithFields(ws.OWI()).WithField("workspace", req.NamespacedName).Errorf("failed to get image info: %v", err) + } else { + glog.WithFields(ws.OWI()).WithField("workspace", req.NamespacedName).WithField("imageInfo", glog.TrustedValueWrap{Value: imageInfo}).Info("updated image info") + } + } + return ctrl.Result{}, wsc.operations.SetupWorkspace(ctx, ws.Name, imageInfo) } func (wsc *WorkspaceController) handleWorkspaceStop(ctx context.Context, ws *workspacev1.Workspace, req ctrl.Request) (result ctrl.Result, err error) { diff --git a/components/ws-daemon/pkg/controller/workspace_operations.go b/components/ws-daemon/pkg/controller/workspace_operations.go index f55d57a5833197..e0d4d96b265ecb 100644 --- a/components/ws-daemon/pkg/controller/workspace_operations.go +++ b/components/ws-daemon/pkg/controller/workspace_operations.go @@ -6,6 +6,7 @@ package controller import ( "context" + "encoding/json" "errors" "fmt" "io/fs" @@ -22,6 +23,7 @@ import ( "github.com/gitpod-io/gitpod/content-service/pkg/storage" "github.com/gitpod-io/gitpod/ws-daemon/pkg/content" "github.com/gitpod-io/gitpod/ws-daemon/pkg/internal/session" + workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1" "github.com/opentracing/opentracing-go" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -71,7 +73,7 @@ type WorkspaceOperations interface { // Snapshot takes a snapshot of the workspace Snapshot(ctx context.Context, instanceID, snapshotName string) (err error) // Setup ensures that the workspace has been setup - SetupWorkspace(ctx context.Context, instanceID string) error + SetupWorkspace(ctx context.Context, instanceID string, imageInfo *workspacev1.WorkspaceImageInfo) error } type DefaultWorkspaceOperations struct { @@ -211,12 +213,15 @@ func (wso *DefaultWorkspaceOperations) creator(owner, workspaceID, instanceID st } } -func (wso *DefaultWorkspaceOperations) SetupWorkspace(ctx context.Context, instanceID string) error { - _, err := wso.provider.GetAndConnect(ctx, instanceID) +func (wso *DefaultWorkspaceOperations) SetupWorkspace(ctx context.Context, instanceID string, imageInfo *workspacev1.WorkspaceImageInfo) error { + ws, err := wso.provider.GetAndConnect(ctx, instanceID) if err != nil { return fmt.Errorf("cannot setup workspace %s: %w", instanceID, err) } - + err = wso.writeImageInfo(ctx, ws, imageInfo) + if err != nil { + glog.WithError(err).WithFields(ws.OWI()).Error("cannot write image info") + } return nil } @@ -513,6 +518,26 @@ func (wso *DefaultWorkspaceOperations) uploadWorkspaceContent(ctx context.Contex return nil } +func (wso *DefaultWorkspaceOperations) writeImageInfo(_ context.Context, ws *session.Workspace, imageInfo *workspacev1.WorkspaceImageInfo) error { + if imageInfo == nil { + return nil + } + + b, err := json.Marshal(imageInfo) + if err != nil { + return fmt.Errorf("cannot marshal image info: %w", err) + } + uid := (wsinit.GitpodUID + 100000 - 1) + gid := (wsinit.GitpodGID + 100000 - 1) + fp := filepath.Join(ws.Location, ".gitpod/image") + err = os.WriteFile(fp, b, 0644) + if err != nil { + return fmt.Errorf("cannot write image info: %w", err) + } + os.Chown(fp, uid, gid) + return nil +} + func retryIfErr(ctx context.Context, attempts int, log *logrus.Entry, op func(ctx context.Context) error) (err error) { //nolint:ineffassign span, ctx := opentracing.StartSpanFromContext(ctx, "retryIfErr") diff --git a/components/ws-daemon/pkg/daemon/daemon.go b/components/ws-daemon/pkg/daemon/daemon.go index 7c3af091fb82df..b62f673f962ef1 100644 --- a/components/ws-daemon/pkg/daemon/daemon.go +++ b/components/ws-daemon/pkg/daemon/daemon.go @@ -214,7 +214,7 @@ func NewDaemon(config Config) (*Daemon, error) { } wsctrl, err := controller.NewWorkspaceController( - mgr.GetClient(), mgr.GetEventRecorderFor("workspace"), nodename, config.Runtime.SecretsNamespace, config.WorkspaceController.MaxConcurrentReconciles, workspaceOps, wrappedReg) + mgr.GetClient(), mgr.GetEventRecorderFor("workspace"), nodename, config.Runtime.SecretsNamespace, config.WorkspaceController.MaxConcurrentReconciles, workspaceOps, wrappedReg, containerRuntime) if err != nil { return nil, err } diff --git a/components/ws-manager-api/go/crd/v1/workspace_types.go b/components/ws-manager-api/go/crd/v1/workspace_types.go index b1af101a6f96c6..9ef7411cd27117 100644 --- a/components/ws-manager-api/go/crd/v1/workspace_types.go +++ b/components/ws-manager-api/go/crd/v1/workspace_types.go @@ -168,6 +168,17 @@ func (ps PortSpec) Equal(other PortSpec) bool { return true } +type WorkspaceImageInfo struct { + // +kubebuilder:validation:Required + TotalSize int64 `json:"totalSize"` + + // +kubebuilder:validation:Optional + WorkspaceImageSize int64 `json:"workspaceImageSize,omitempty"` + + // +kubebuilder:validation:Optional + WorkspaceImageRef string `json:"workspaceImageRef,omitempty"` +} + // WorkspaceStatus defines the observed state of Workspace type WorkspaceStatus struct { PodStarts int `json:"podStarts"` @@ -193,6 +204,9 @@ type WorkspaceStatus struct { Storage StorageStatus `json:"storage,omitempty"` LastActivity *metav1.Time `json:"lastActivity,omitempty"` + + // +kubebuilder:validation:Optional + ImageInfo *WorkspaceImageInfo `json:"imageInfo,omitempty"` } func (s *WorkspaceStatus) SetCondition(cond metav1.Condition) { diff --git a/components/ws-manager-api/go/crd/v1/zz_generated.deepcopy.go b/components/ws-manager-api/go/crd/v1/zz_generated.deepcopy.go index 77f8ce2c2e1f92..0a92d5461c5651 100644 --- a/components/ws-manager-api/go/crd/v1/zz_generated.deepcopy.go +++ b/components/ws-manager-api/go/crd/v1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated // Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). @@ -306,6 +305,21 @@ func (in *WorkspaceImage) DeepCopy() *WorkspaceImage { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceImageInfo) DeepCopyInto(out *WorkspaceImageInfo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceImageInfo. +func (in *WorkspaceImageInfo) DeepCopy() *WorkspaceImageInfo { + if in == nil { + return nil + } + out := new(WorkspaceImageInfo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceImages) DeepCopyInto(out *WorkspaceImages) { *out = *in @@ -448,6 +462,11 @@ func (in *WorkspaceStatus) DeepCopyInto(out *WorkspaceStatus) { in, out := &in.LastActivity, &out.LastActivity *out = (*in).DeepCopy() } + if in.ImageInfo != nil { + in, out := &in.ImageInfo, &out.ImageInfo + *out = new(WorkspaceImageInfo) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceStatus. diff --git a/components/ws-manager-mk2/Makefile b/components/ws-manager-mk2/Makefile index 65066295b6463e..c3fd22be82a3f4 100644 --- a/components/ws-manager-mk2/Makefile +++ b/components/ws-manager-mk2/Makefile @@ -134,7 +134,7 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions KUSTOMIZE_VERSION ?= v3.8.7 -CONTROLLER_TOOLS_VERSION ?= v0.10.0 +CONTROLLER_TOOLS_VERSION ?= v0.16.5 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize diff --git a/components/ws-manager-mk2/config/crd/bases/workspace.gitpod.io_snapshots.yaml b/components/ws-manager-mk2/config/crd/bases/workspace.gitpod.io_snapshots.yaml index eed6a224efae3a..9153f1bfd33813 100644 --- a/components/ws-manager-mk2/config/crd/bases/workspace.gitpod.io_snapshots.yaml +++ b/components/ws-manager-mk2/config/crd/bases/workspace.gitpod.io_snapshots.yaml @@ -7,8 +7,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: snapshots.workspace.gitpod.io spec: group: workspace.gitpod.io @@ -38,14 +37,19 @@ spec: description: Snapshot is the Schema for the snapshot API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object diff --git a/components/ws-manager-mk2/config/crd/bases/workspace.gitpod.io_workspaces.yaml b/components/ws-manager-mk2/config/crd/bases/workspace.gitpod.io_workspaces.yaml index 72dbd0d7179c95..8a71373e272fb1 100644 --- a/components/ws-manager-mk2/config/crd/bases/workspace.gitpod.io_workspaces.yaml +++ b/components/ws-manager-mk2/config/crd/bases/workspace.gitpod.io_workspaces.yaml @@ -7,8 +7,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: workspaces.workspace.gitpod.io spec: group: workspace.gitpod.io @@ -53,14 +52,19 @@ spec: description: Workspace is the Schema for the workspaces API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -176,15 +180,16 @@ spec: description: Name of the environment variable. Must be a C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's value. Cannot @@ -197,8 +202,9 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: Specify whether the ConfigMap or its key @@ -209,10 +215,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the FieldPath is @@ -227,10 +232,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required for volumes, @@ -259,8 +263,9 @@ spec: be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: Specify whether the Secret or its key must @@ -302,15 +307,16 @@ spec: description: Name of the environment variable. Must be a C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's value. Cannot @@ -323,8 +329,9 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: Specify whether the ConfigMap or its key @@ -335,10 +342,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the FieldPath is @@ -353,10 +359,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required for volumes, @@ -385,8 +390,9 @@ spec: be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: description: Specify whether the Secret or its key must @@ -418,43 +424,35 @@ spec: properties: conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 @@ -469,10 +467,6 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -524,6 +518,19 @@ spec: type: string type: array type: object + imageInfo: + properties: + totalSize: + format: int64 + type: integer + workspaceImageRef: + type: string + workspaceImageSize: + format: int64 + type: integer + required: + - totalSize + type: object lastActivity: format: date-time type: string diff --git a/components/ws-manager-mk2/config/webhook/manifests.yaml b/components/ws-manager-mk2/config/webhook/manifests.yaml index 9c594e2717cb89..7da836094b1d47 100644 --- a/components/ws-manager-mk2/config/webhook/manifests.yaml +++ b/components/ws-manager-mk2/config/webhook/manifests.yaml @@ -6,7 +6,6 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -33,7 +32,6 @@ webhooks: apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: diff --git a/install/installer/go.mod b/install/installer/go.mod index fcf6de7ca9461b..16bf9e56f7fe94 100644 --- a/install/installer/go.mod +++ b/install/installer/go.mod @@ -101,6 +101,7 @@ require ( github.com/containerd/continuity v0.4.2 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/containerd/ttrpc v1.2.2 // indirect github.com/containerd/typeurl/v2 v2.1.1 // indirect github.com/containers/storage v1.39.0 // indirect @@ -223,7 +224,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.10 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/opencontainers/selinux v1.11.0 // indirect diff --git a/install/installer/go.sum b/install/installer/go.sum index c9e63b29097d4d..3baeeca446ff1a 100644 --- a/install/installer/go.sum +++ b/install/installer/go.sum @@ -1088,6 +1088,8 @@ github.com/containerd/imgcrypt v1.1.7/go.mod h1:FD8gqIcX5aTotCtOmjeCsi3A1dHmTZpn github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/nri v0.4.0/go.mod h1:Zw9q2lP16sdg0zYybemZ9yTDy8g7fPCIB3KXOGlggXI= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.11.3/go.mod h1:7vRJIcImfY8bpifnMjt+HTJoQxASq7T28MYbP15/Nf0= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= @@ -2170,8 +2172,9 @@ github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=