From d0213f54b35de9e7596e457ca6652e992a9c3afd Mon Sep 17 00:00:00 2001 From: yy Date: Sat, 19 Jul 2025 12:03:16 +0000 Subject: [PATCH 01/46] feat: devbox daemon controller, devbox state change handler --- controllers/devbox/Makefile | 2 +- controllers/devbox/PROJECT | 34 - .../devbox/api/v1alpha1/devbox_types.go | 98 +- .../api/v1alpha1/devboxrelease_types.go | 80 -- .../api/v1alpha1/operationrequest_types.go | 61 -- .../api/v1alpha1/zz_generated.deepcopy.go | 265 +---- controllers/devbox/cmd/main.go | 77 +- .../crd/bases/devbox.sealos.io_devboxes.yaml | 921 ++++++++++-------- .../devbox.sealos.io_devboxreleases.yaml | 92 -- .../devbox.sealos.io_operationrequests.yaml | 63 -- .../devbox/config/crd/kustomization.yaml | 4 - .../rbac/devboxrelease_editor_role.yaml | 41 - .../rbac/devboxrelease_viewer_role.yaml | 37 - .../devbox/config/rbac/kustomization.yaml | 5 - .../rbac/operationrequest_editor_role.yaml | 41 - .../rbac/operationrequest_viewer_role.yaml | 37 - controllers/devbox/config/rbac/role.yaml | 85 +- .../samples/devbox_v1alpha1_devbox.yaml | 19 +- .../devbox/config/samples/kustomization.yaml | 4 - .../samples/test/devbox_v1alpha1_devbox.yaml | 33 +- controllers/devbox/go.mod | 18 +- controllers/devbox/go.sum | 32 +- controllers/devbox/internal/commit/commit.go | 26 + .../internal/controller/devbox_controller.go | 315 +++--- .../controller/devboxrelease_controller.go | 171 ---- .../devboxrelease_controller_test.go | 84 -- .../internal/controller/helper/devbox.go | 220 +---- .../controller/state_change_handler.go | 158 +++ .../controller/utils/matcher/matcher.go | 8 + .../internal/controller/utils/nodes/node.go | 14 + controllers/go.work.sum | 79 +- 31 files changed, 1167 insertions(+), 1957 deletions(-) delete mode 100644 controllers/devbox/api/v1alpha1/devboxrelease_types.go delete mode 100644 controllers/devbox/api/v1alpha1/operationrequest_types.go delete mode 100644 controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml delete mode 100644 controllers/devbox/config/crd/bases/devbox.sealos.io_operationrequests.yaml delete mode 100644 controllers/devbox/config/rbac/devboxrelease_editor_role.yaml delete mode 100644 controllers/devbox/config/rbac/devboxrelease_viewer_role.yaml delete mode 100644 controllers/devbox/config/rbac/operationrequest_editor_role.yaml delete mode 100644 controllers/devbox/config/rbac/operationrequest_viewer_role.yaml create mode 100644 controllers/devbox/internal/commit/commit.go delete mode 100644 controllers/devbox/internal/controller/devboxrelease_controller.go delete mode 100644 controllers/devbox/internal/controller/devboxrelease_controller_test.go create mode 100644 controllers/devbox/internal/controller/state_change_handler.go create mode 100644 controllers/devbox/internal/controller/utils/nodes/node.go diff --git a/controllers/devbox/Makefile b/controllers/devbox/Makefile index 1406744c42ec..30963cf60ee3 100644 --- a/controllers/devbox/Makefile +++ b/controllers/devbox/Makefile @@ -127,7 +127,7 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions KUSTOMIZE_VERSION ?= v5.3.0 -CONTROLLER_TOOLS_VERSION ?= v0.14.0 +CONTROLLER_TOOLS_VERSION ?= v0.16.0 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize diff --git a/controllers/devbox/PROJECT b/controllers/devbox/PROJECT index b93fe52f916d..fb2db11da984 100644 --- a/controllers/devbox/PROJECT +++ b/controllers/devbox/PROJECT @@ -17,38 +17,4 @@ resources: kind: Devbox path: github.com/labring/sealos/controllers/devbox/api/v1alpha1 version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - domain: sealos.io - group: devbox - kind: Runtime - path: github.com/labring/sealos/controllers/devbox/api/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - domain: sealos.io - group: devbox - kind: RuntimeClass - path: github.com/labring/sealos/controllers/devbox/api/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - controller: true - domain: sealos.io - group: devbox - kind: DevBoxRelease - path: github.com/labring/sealos/controllers/devbox/api/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - controller: true - domain: sealos.io - group: devbox - kind: OperationRequest - path: github.com/labring/sealos/controllers/devbox/api/v1alpha1 - version: v1alpha1 version: "3" diff --git a/controllers/devbox/api/v1alpha1/devbox_types.go b/controllers/devbox/api/v1alpha1/devbox_types.go index c0aade1d6fbb..3cc78eedfdfe 100644 --- a/controllers/devbox/api/v1alpha1/devbox_types.go +++ b/controllers/devbox/api/v1alpha1/devbox_types.go @@ -24,7 +24,16 @@ import ( const ( // FinalizerName is the finalizer for Devbox FinalizerName = "devbox.sealos.io/finalizer" - DevBoxPartOf = "devbox" + + // Annotate the devbox pod with the devbox init + AnnotationInit = "devbox.sealos.io/init" + // Annotate the devbox pod with the storage limit + AnnotationStorageLimit = "devbox.sealos.io/storage-limit" + // Annotate the devbox pod with the devbox part of + AnnotationContentID = "devbox.sealos.io/content-id" + + // Label the devbox pod with the devbox part of + LabelDevBoxPartOf = "devbox" ) type DevboxState string @@ -58,8 +67,8 @@ type NetworkSpec struct { // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=NodePort;Tailnet Type NetworkType `json:"type"` - // +kubebuilder:validation:Optional - ExtraPorts []corev1.ContainerPort `json:"extraPorts"` + // // +kubebuilder:validation:Optional + // ExtraPorts []corev1.ContainerPort `json:"extraPorts,omitempty"` } type Config struct { @@ -90,12 +99,10 @@ type Config struct { ReleaseArgs []string `json:"releaseArgs,omitempty"` // TODO: in v1alpha2 api we need fix the port and app port into one field and create a new type for it. - // +kubebuilder:validation:Optional - // +kubebuilder:default={{name:"devbox-ssh-port",containerPort:22,protocol:TCP}} - Ports []corev1.ContainerPort `json:"ports,omitempty"` - // +kubebuilder:validation:Optional - // +kubebuilder:default={{name:"devbox-app-port",port:8080,protocol:TCP}} - AppPorts []corev1.ServicePort `json:"appPorts,omitempty"` + // // +kubebuilder:validation:Optional + // Ports []corev1.ContainerPort `json:"ports,omitempty"` + // // +kubebuilder:validation:Optional + // AppPorts []corev1.ServicePort `json:"appPorts,omitempty"` // +kubebuilder:validation:Optional VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` @@ -107,6 +114,7 @@ type Config struct { type DevboxSpec struct { // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=Running;Stopped;Shutdown + // +kubebuilder:default=Running State DevboxState `json:"state"` // +kubebuilder:validation:Required Resource corev1.ResourceList `json:"resource"` @@ -124,6 +132,10 @@ type DevboxSpec struct { // +kubebuilder:validation:Required Config Config `json:"config"` + // +kubebuilder:validation:Optional + // devbox storage limit, `storageLimit` will be used to generate the devbox pod label. + StorageLimit string `json:"storageLimit,omitempty"` + // +kubebuilder:validation:Required NetworkSpec NetworkSpec `json:"network,omitempty"` @@ -155,27 +167,9 @@ type CommitStatus string const ( CommitStatusSuccess CommitStatus = "Success" CommitStatusFailed CommitStatus = "Failed" - CommitStatusUnknown CommitStatus = "Unknown" CommitStatusPending CommitStatus = "Pending" ) -type CommitHistory struct { - // Image is the image of the commit - Image string `json:"image"` - // Time is the time when the commit is created - Time metav1.Time `json:"time"` - // Pod is the pod name - Pod string `json:"pod"` - // status will be set based on expectedStatus after devbox pod delete or stop. if expectedStatus is still pending, it means the pod is not running successfully, so we need to set it to `failed` - Status CommitStatus `json:"status"` - // predicatedStatus default `pending`, will be set to `success` if pod status is running successfully. - PredicatedStatus CommitStatus `json:"predicatedStatus"` - // Node is the node name - Node string `json:"node"` - // ContainerID is the container id - ContainerID string `json:"containerID"` -} - type DevboxPhase string const ( @@ -197,19 +191,57 @@ const ( DevboxPhaseUnknown DevboxPhase = "Unknown" ) +type CommitRecord struct { + // BaseImage is the image of the that devbox is running on + // +kubebuilder:validation:Optional + BaseImage string `json:"baseImage"` + + // CommitImage is the image of the that devbox is committed to + // +kubebuilder:validation:Optional + CommitImage string `json:"commitImage"` + + // Node is the node name + // +kubebuilder:validation:Optional + Node string `json:"node"` + + // GenerateTime is the time when the commit is generated + // +kubebuilder:validation:Optional + GenerateTime metav1.Time `json:"generateTime"` + + // ScheduleTime is the time when the commit is scheduled + // +kubebuilder:validation:Optional + ScheduleTime metav1.Time `json:"scheduleTime"` + + // UpdateTime is the time when the commit is updated + // +kubebuilder:validation:Optional + UpdateTime metav1.Time `json:"updateTime"` + + // CommitTime is the time when the commit is created + // +kubebuilder:validation:Optional + CommitTime metav1.Time `json:"commitTime"` + + // CommitStatus is the status of the commit + // +kubebuilder:validation:Enum=Success;Failed;Pending + // +kubebuilder:default=Pending + CommitStatus CommitStatus `json:"commitStatus"` +} + +// CommitRecordMap is a map of commit records, key is the commit id +type CommitRecordMap map[string]*CommitRecord + // DevboxStatus defines the observed state of Devbox type DevboxStatus struct { // +kubebuilder:validation:Optional - Network NetworkStatus `json:"network"` + ContentID string `json:"contentID"` // +kubebuilder:validation:Optional - CommitHistory []*CommitHistory `json:"commitHistory"` + // +kubebuilder:default=Running + State DevboxState `json:"state"` + // CommitRecords is the records of the devbox commits + CommitRecords CommitRecordMap `json:"commitRecords"` // +kubebuilder:validation:Optional Phase DevboxPhase `json:"phase"` - // +kubebuilder:validation:Optional - State corev1.ContainerState `json:"state"` - // +kubebuilder:validation:Optional - LastTerminationState corev1.ContainerState `json:"lastState"` + Network NetworkStatus `json:"network"` } // +kubebuilder:object:root=true diff --git a/controllers/devbox/api/v1alpha1/devboxrelease_types.go b/controllers/devbox/api/v1alpha1/devboxrelease_types.go deleted file mode 100644 index 7ac9e1267735..000000000000 --- a/controllers/devbox/api/v1alpha1/devboxrelease_types.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2024. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// DevBoxReleaseSpec defines the desired state of DevBoxRelease -type DevBoxReleaseSpec struct { - // +kubebuilder:validation:Required - DevboxName string `json:"devboxName"` - // +kubebuilder:validation:Required - NewTag string `json:"newTag"` - // +kubebuilder:validation:Optional - Notes string `json:"notes,omitempty"` -} - -type DevboxReleasePhase string - -const ( - // DevboxReleasePhaseSuccess means the Devbox has been tagged - DevboxReleasePhaseSuccess DevboxReleasePhase = "Success" - // DevboxReleasePhasePending means the Devbox has not been tagged - DevboxReleasePhasePending DevboxReleasePhase = "Pending" - // DevboxReleasePhaseFailed means the Devbox has not been tagged - DevboxReleasePhaseFailed DevboxReleasePhase = "Failed" -) - -// DevBoxReleaseStatus defines the observed state of DevBoxRelease -type DevBoxReleaseStatus struct { - // +kubebuilder:validation:Optional - // +kubebuilder:default=Pending - Phase DevboxReleasePhase `json:"phase"` - // +kubebuilder:validation:Optional - OriginalImage string `json:"originalImage"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:name="DevboxName",type="string",JSONPath=".spec.devboxName" -// +kubebuilder:printcolumn:name="NewTag",type="string",JSONPath=".spec.newTag" -// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" -// +kubebuilder:printcolumn:name="OriginalImage",type="string",JSONPath=".status.originalImage" - -// DevBoxRelease is the Schema for the devboxreleases API -type DevBoxRelease struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec DevBoxReleaseSpec `json:"spec,omitempty"` - Status DevBoxReleaseStatus `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// DevBoxReleaseList contains a list of DevBoxRelease -type DevBoxReleaseList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []DevBoxRelease `json:"items"` -} - -func init() { - SchemeBuilder.Register(&DevBoxRelease{}, &DevBoxReleaseList{}) -} diff --git a/controllers/devbox/api/v1alpha1/operationrequest_types.go b/controllers/devbox/api/v1alpha1/operationrequest_types.go deleted file mode 100644 index 92b236e54adc..000000000000 --- a/controllers/devbox/api/v1alpha1/operationrequest_types.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2024. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// OperationRequestSpec defines the desired state of OperationRequest -type OperationRequestSpec struct { -} - -// OperationRequestStatus defines the observed state of OperationRequest -type OperationRequestStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - //+kubebuilder:default:=Pending - //+kubebuilder:validation:Enum=Pending;Processing;Completed;Failed -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status - -// OperationRequest is the Schema for the operationrequests API -type OperationRequest struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec OperationRequestSpec `json:"spec,omitempty"` - Status OperationRequestStatus `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// OperationRequestList contains a list of OperationRequest -type OperationRequestList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []OperationRequest `json:"items"` -} - -func init() { - SchemeBuilder.Register(&OperationRequest{}, &OperationRequestList{}) -} diff --git a/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go b/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go index d1590df7e111..f3f49636710c 100644 --- a/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go +++ b/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go @@ -21,26 +21,59 @@ limitations under the License. package v1alpha1 import ( - v1 "k8s.io/api/core/v1" + "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommitHistory) DeepCopyInto(out *CommitHistory) { +func (in *CommitRecord) DeepCopyInto(out *CommitRecord) { *out = *in - in.Time.DeepCopyInto(&out.Time) + in.GenerateTime.DeepCopyInto(&out.GenerateTime) + in.ScheduleTime.DeepCopyInto(&out.ScheduleTime) + in.UpdateTime.DeepCopyInto(&out.UpdateTime) + in.CommitTime.DeepCopyInto(&out.CommitTime) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommitHistory. -func (in *CommitHistory) DeepCopy() *CommitHistory { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommitRecord. +func (in *CommitRecord) DeepCopy() *CommitRecord { if in == nil { return nil } - out := new(CommitHistory) + out := new(CommitRecord) in.DeepCopyInto(out) return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in CommitRecordMap) DeepCopyInto(out *CommitRecordMap) { + { + in := &in + *out = make(CommitRecordMap, len(*in)) + for key, val := range *in { + var outVal *CommitRecord + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = new(CommitRecord) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommitRecordMap. +func (in CommitRecordMap) DeepCopy() CommitRecordMap { + if in == nil { + return nil + } + out := new(CommitRecordMap) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Config) DeepCopyInto(out *Config) { *out = *in @@ -85,18 +118,6 @@ func (in *Config) DeepCopyInto(out *Config) { *out = make([]string, len(*in)) copy(*out, *in) } - if in.Ports != nil { - in, out := &in.Ports, &out.Ports - *out = make([]v1.ContainerPort, len(*in)) - copy(*out, *in) - } - if in.AppPorts != nil { - in, out := &in.AppPorts, &out.AppPorts - *out = make([]v1.ServicePort, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } if in.VolumeMounts != nil { in, out := &in.VolumeMounts, &out.VolumeMounts *out = make([]v1.VolumeMount, len(*in)) @@ -123,95 +144,6 @@ func (in *Config) DeepCopy() *Config { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevBoxRelease) DeepCopyInto(out *DevBoxRelease) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevBoxRelease. -func (in *DevBoxRelease) DeepCopy() *DevBoxRelease { - if in == nil { - return nil - } - out := new(DevBoxRelease) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DevBoxRelease) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevBoxReleaseList) DeepCopyInto(out *DevBoxReleaseList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]DevBoxRelease, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevBoxReleaseList. -func (in *DevBoxReleaseList) DeepCopy() *DevBoxReleaseList { - if in == nil { - return nil - } - out := new(DevBoxReleaseList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DevBoxReleaseList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevBoxReleaseSpec) DeepCopyInto(out *DevBoxReleaseSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevBoxReleaseSpec. -func (in *DevBoxReleaseSpec) DeepCopy() *DevBoxReleaseSpec { - if in == nil { - return nil - } - out := new(DevBoxReleaseSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevBoxReleaseStatus) DeepCopyInto(out *DevBoxReleaseStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevBoxReleaseStatus. -func (in *DevBoxReleaseStatus) DeepCopy() *DevBoxReleaseStatus { - if in == nil { - return nil - } - out := new(DevBoxReleaseStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Devbox) DeepCopyInto(out *Devbox) { *out = *in @@ -282,7 +214,7 @@ func (in *DevboxSpec) DeepCopyInto(out *DevboxSpec) { } } in.Config.DeepCopyInto(&out.Config) - in.NetworkSpec.DeepCopyInto(&out.NetworkSpec) + out.NetworkSpec = in.NetworkSpec if in.NodeSelector != nil { in, out := &in.NodeSelector, &out.NodeSelector *out = make(map[string]string, len(*in)) @@ -317,20 +249,23 @@ func (in *DevboxSpec) DeepCopy() *DevboxSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DevboxStatus) DeepCopyInto(out *DevboxStatus) { *out = *in - out.Network = in.Network - if in.CommitHistory != nil { - in, out := &in.CommitHistory, &out.CommitHistory - *out = make([]*CommitHistory, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(CommitHistory) + if in.CommitRecords != nil { + in, out := &in.CommitRecords, &out.CommitRecords + *out = make(CommitRecordMap, len(*in)) + for key, val := range *in { + var outVal *CommitRecord + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = new(CommitRecord) (*in).DeepCopyInto(*out) } + (*out)[key] = outVal } } - in.State.DeepCopyInto(&out.State) - in.LastTerminationState.DeepCopyInto(&out.LastTerminationState) + out.Network = in.Network } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxStatus. @@ -346,11 +281,6 @@ func (in *DevboxStatus) DeepCopy() *DevboxStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) { *out = *in - if in.ExtraPorts != nil { - in, out := &in.ExtraPorts, &out.ExtraPorts - *out = make([]v1.ContainerPort, len(*in)) - copy(*out, *in) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkSpec. @@ -378,95 +308,6 @@ func (in *NetworkStatus) DeepCopy() *NetworkStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OperationRequest) DeepCopyInto(out *OperationRequest) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperationRequest. -func (in *OperationRequest) DeepCopy() *OperationRequest { - if in == nil { - return nil - } - out := new(OperationRequest) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *OperationRequest) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OperationRequestList) DeepCopyInto(out *OperationRequestList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]OperationRequest, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperationRequestList. -func (in *OperationRequestList) DeepCopy() *OperationRequestList { - if in == nil { - return nil - } - out := new(OperationRequestList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *OperationRequestList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OperationRequestSpec) DeepCopyInto(out *OperationRequestSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperationRequestSpec. -func (in *OperationRequestSpec) DeepCopy() *OperationRequestSpec { - if in == nil { - return nil - } - out := new(OperationRequestSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OperationRequestStatus) DeepCopyInto(out *OperationRequestStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperationRequestStatus. -func (in *OperationRequestStatus) DeepCopy() *OperationRequestStatus { - if in == nil { - return nil - } - out := new(OperationRequestStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RuntimeRef) DeepCopyInto(out *RuntimeRef) { *out = *in diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 01e23446353d..8908d9e4c163 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "context" "crypto/tls" "flag" "os" @@ -26,6 +27,7 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" "k8s.io/utils/ptr" corev1 "k8s.io/api/core/v1" @@ -45,9 +47,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + "github.com/labring/sealos/controllers/devbox/internal/commit" "github.com/labring/sealos/controllers/devbox/internal/controller" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" - "github.com/labring/sealos/controllers/devbox/internal/controller/utils/registry" + "github.com/labring/sealos/controllers/devbox/internal/controller/utils/nodes" utilresource "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" // +kubebuilder:scaffold:imports ) @@ -66,7 +69,6 @@ func init() { func main() { var metricsAddr string - var enableLeaderElection bool var probeAddr string var secureMetrics bool var enableHTTP2 bool @@ -88,17 +90,17 @@ func main() { var enablePodEnvMatcher bool var enablePodPortMatcher bool var enablePodEphemeralStorageMatcher bool + var enablePodStorageLimitMatcher bool // config qps and burst var configQPS int var configBurst int // config restart predicate duration var restartPredicateDuration time.Duration + // devbox node label + var devboxNodeLabel string flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") - flag.BoolVar(&enableLeaderElection, "leader-elect", false, - "Enable leader election for controller manager. "+ - "Enabling this will ensure there is only one active controller manager.") flag.BoolVar(&secureMetrics, "metrics-secure", true, "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") flag.BoolVar(&enableHTTP2, "enable-http2", false, @@ -120,11 +122,14 @@ func main() { flag.BoolVar(&enablePodEnvMatcher, "enable-pod-env-matcher", true, "If set, pod env matcher will be enabled") flag.BoolVar(&enablePodPortMatcher, "enable-pod-port-matcher", true, "If set, pod port matcher will be enabled") flag.BoolVar(&enablePodEphemeralStorageMatcher, "enable-pod-ephemeral-storage-matcher", false, "If set, pod ephemeral storage matcher will be enabled") + flag.BoolVar(&enablePodStorageLimitMatcher, "enable-pod-storage-limit-matcher", false, "If set, pod storage limit matcher will be enabled") // config qps and burst flag.IntVar(&configQPS, "config-qps", 50, "The qps of the config") flag.IntVar(&configBurst, "config-burst", 100, "The burst of the config") // config restart predicate duration flag.DurationVar(&restartPredicateDuration, "restart-predicate-duration", 2*time.Hour, "Sets the restart predicate time duration for devbox controller restart. By default, the duration is set to 2 hours.") + // devbox node label + flag.StringVar(&devboxNodeLabel, "devbox-node-label", "devbox.sealos.io/node", "The label of the devbox node") opts := zap.Options{ Development: true, } @@ -191,8 +196,8 @@ func main() { Metrics: metricsServerOptions, WebhookServer: webhookServer, HealthProbeBindAddress: probeAddr, - LeaderElection: enableLeaderElection, - LeaderElectionID: "b6694722.sealos.io", + LeaderElection: false, + // LeaderElectionID: "b6694722.sealos.io", // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly @@ -235,12 +240,20 @@ func main() { if enablePodEphemeralStorageMatcher { podMatchers = append(podMatchers, matcher.EphemeralStorageMatcher{}) } + if enablePodStorageLimitMatcher { + podMatchers = append(podMatchers, matcher.StorageLimitMatcher{}) + } + + stateChangeBroadcaster := record.NewBroadcaster() if err = (&controller.DevboxReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("devbox-controller"), + StateChangeRecorder: stateChangeBroadcaster.NewRecorder( + mgr.GetScheme(), + corev1.EventSource{Component: "devbox-controller", Host: nodes.GetNodeName()}), CommitImageRegistry: registryAddr, - Recorder: mgr.GetEventRecorderFor("devbox-controller"), RequestRate: utilresource.RequestRate{ CPU: requestCPURate, Memory: requestMemoryRate, @@ -253,30 +266,38 @@ func main() { PodMatchers: podMatchers, DebugMode: debugMode, RestartPredicateDuration: restartPredicateDuration, + NodeName: nodes.GetNodeName(), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Devbox") os.Exit(1) } - if err = (&controller.DevBoxReleaseReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Registry: ®istry.Client{ - Username: registryUser, - Password: registryPassword, - }, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "DevBoxRelease") - os.Exit(1) + committer := &commit.CommitterImpl{} + + stateChangeHandler := controller.StateChangeHandler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("state-change-handler"), + Committer: committer, + CommitImageRegistry: registryAddr, + NodeName: nodes.GetNodeName(), + Logger: ctrl.Log.WithName("state-change-handler"), } - //if err = (&controller.OperationRequestReconciler{ - // CommitImageRegistry: registryAddr, - // Client: mgr.GetClient(), - // Scheme: mgr.GetScheme(), - //}).SetupWithManager(mgr); err != nil { - // setupLog.Error(err, "unable to create controller", "controller", "OperationRequest") - // os.Exit(1) - //} + + // 添加调试日志 + setupLog.Info("StateChangeHandler initialized", + "nodeName", nodes.GetNodeName(), + "registryAddr", registryAddr) + + watcher := stateChangeBroadcaster.StartEventWatcher(func(event *corev1.Event) { + setupLog.Info("Event received by watcher", + "event", event.Name, + "eventSourceHost", event.Source.Host, + "eventType", event.Type, + "eventReason", event.Reason) + stateChangeHandler.Handle(context.Background(), event) + }) + defer watcher.Stop() // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index b9f6b65cb948..7baa32a093a9 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -1,23 +1,9 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.0 name: devboxes.devbox.sealos.io spec: group: devbox.sealos.io @@ -120,11 +106,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. @@ -152,11 +140,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic weight: @@ -169,6 +159,7 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the affinity requirements specified by this field are not met at @@ -213,11 +204,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchFields: description: A list of node selector requirements by node's fields. @@ -245,14 +238,17 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic type: object x-kubernetes-map-type: atomic type: array + x-kubernetes-list-type: atomic required: - nodeSelectorTerms type: object @@ -282,8 +278,9 @@ spec: with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label @@ -312,11 +309,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -327,6 +326,36 @@ spec: type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: description: |- A label query over the set of namespaces that the term applies to. @@ -362,11 +391,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -386,6 +417,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -408,6 +440,7 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the affinity requirements specified by this field are not met at @@ -427,8 +460,9 @@ spec: a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label @@ -456,11 +490,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -471,6 +507,36 @@ spec: type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: description: |- A label query over the set of namespaces that the term applies to. @@ -505,11 +571,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -529,6 +597,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -541,6 +610,7 @@ spec: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object podAntiAffinity: description: Describes pod anti-affinity scheduling rules (e.g. @@ -567,8 +637,9 @@ spec: with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label @@ -597,11 +668,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -612,6 +685,36 @@ spec: type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: description: |- A label query over the set of namespaces that the term applies to. @@ -647,11 +750,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -671,6 +776,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -693,6 +799,7 @@ spec: - weight type: object type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: description: |- If the anti-affinity requirements specified by this field are not met at @@ -712,8 +819,9 @@ spec: a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. properties: matchExpressions: description: matchExpressions is a list of label @@ -741,11 +849,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -756,6 +866,36 @@ spec: type: object type: object x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic namespaceSelector: description: |- A label query over the set of namespaces that the term applies to. @@ -790,11 +930,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -814,6 +956,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic topologyKey: description: |- This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching @@ -826,6 +969,7 @@ spec: - topologyKey type: object type: array + x-kubernetes-list-type: atomic type: object type: object config: @@ -834,84 +978,6 @@ spec: additionalProperties: type: string type: object - appPorts: - default: - - name: devbox-app-port - port: 8080 - protocol: TCP - items: - description: ServicePort contains information on service's port. - properties: - appProtocol: - description: |- - The application protocol for this port. - This is used as a hint for implementations to offer richer behavior for protocols that they understand. - This field follows standard Kubernetes label syntax. - Valid values are either: - - - * Un-prefixed protocol names - reserved for IANA standard service names (as per - RFC-6335 and https://www.iana.org/assignments/service-names). - - - * Kubernetes-defined prefixed names: - * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540 - * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455 - * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455 - - - * Other protocols should use implementation-defined prefixed names such as - mycompany.com/my-custom-protocol. - type: string - name: - description: |- - The name of this port within the service. This must be a DNS_LABEL. - All ports within a ServiceSpec must have unique names. When considering - the endpoints for a Service, this must match the 'name' field in the - EndpointPort. - Optional if only one ServicePort is defined on this service. - type: string - nodePort: - description: |- - The port on each node on which this service is exposed when type is - NodePort or LoadBalancer. Usually assigned by the system. If a value is - specified, in-range, and not in use it will be used, otherwise the - operation will fail. If not specified, a port will be allocated if this - Service requires one. If this field is specified when creating a - Service which does not need it, creation will fail. This field will be - wiped when updating a Service to no longer need it (e.g. changing type - from NodePort to ClusterIP). - More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport - format: int32 - type: integer - port: - description: The port that will be exposed by this service. - format: int32 - type: integer - protocol: - default: TCP - description: |- - The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". - Default is TCP. - type: string - targetPort: - anyOf: - - type: integer - - type: string - description: |- - Number or name of the port to access on the pods targeted by the service. - Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. - If this is a string, it will be looked up as a named port in the - target Pod's container ports. If this is not specified, the value - of the 'port' field is used (an identity map). - This field is ignored for services with clusterIP=None, and should be - omitted or set equal to the 'port' field. - More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array args: description: kubebuilder:validation:Optional items: @@ -953,10 +1019,13 @@ spec: description: The key to select. type: string name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap or its @@ -1016,10 +1085,13 @@ spec: be a valid secret key. type: string name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or its key @@ -1038,50 +1110,6 @@ spec: additionalProperties: type: string type: object - ports: - default: - - containerPort: 22 - name: devbox-ssh-port - protocol: TCP - description: 'TODO: in v1alpha2 api we need fix the port and app - port into one field and create a new type for it.' - items: - description: ContainerPort represents a network port in a single - container. - properties: - containerPort: - description: |- - Number of port to expose on the pod's IP address. - This must be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external port to. - type: string - hostPort: - description: |- - Number of port to expose on the host. - If specified, this must be a valid port number, 0 < x < 65536. - If HostNetwork is specified, this must match ContainerPort. - Most containers do not need this. - format: int32 - type: integer - name: - description: |- - If specified, this must be an IANA_SVC_NAME and unique within the pod. Each - named port in a pod must have a unique name. Name for the port that can be - referred to by services. - type: string - protocol: - default: TCP - description: |- - Protocol for port. Must be UDP, TCP, or SCTP. - Defaults to "TCP". - type: string - required: - - containerPort - type: object - type: array releaseArgs: default: - /home/devbox/project/entrypoint.sh @@ -1114,6 +1142,8 @@ spec: to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. @@ -1123,6 +1153,25 @@ spec: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: description: |- Path within the volume from which the container's volume should be mounted. @@ -1149,6 +1198,8 @@ spec: description: |- awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. + Deprecated: AWSElasticBlockStore is deprecated. All operations for the in-tree + awsElasticBlockStore type are redirected to the ebs.csi.aws.com CSI driver. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore properties: fsType: @@ -1157,7 +1208,6 @@ spec: Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from compromising the machine type: string partition: description: |- @@ -1181,8 +1231,10 @@ spec: - volumeID type: object azureDisk: - description: azureDisk represents an Azure Data Disk mount - on the host and bind mount to the pod. + description: |- + azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. + Deprecated: AzureDisk is deprecated. All operations for the in-tree azureDisk type + are redirected to the disk.csi.azure.com CSI driver. properties: cachingMode: description: 'cachingMode is the Host Caching mode: @@ -1197,6 +1249,7 @@ spec: blob storage type: string fsType: + default: ext4 description: |- fsType is Filesystem type to mount. Must be a filesystem type supported by the host operating system. @@ -1210,6 +1263,7 @@ spec: to shared' type: string readOnly: + default: false description: |- readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. @@ -1219,8 +1273,10 @@ spec: - diskURI type: object azureFile: - description: azureFile represents an Azure File Service - mount on the host and bind mount to the pod. + description: |- + azureFile represents an Azure File Service mount on the host and bind mount to the pod. + Deprecated: AzureFile is deprecated. All operations for the in-tree azureFile type + are redirected to the file.csi.azure.com CSI driver. properties: readOnly: description: |- @@ -1239,8 +1295,9 @@ spec: - shareName type: object cephfs: - description: cephFS represents a Ceph FS mount on the host - that shares a pod's lifetime + description: |- + cephFS represents a Ceph FS mount on the host that shares a pod's lifetime. + Deprecated: CephFS is deprecated and the in-tree cephfs type is no longer supported. properties: monitors: description: |- @@ -1249,6 +1306,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' @@ -1270,10 +1328,13 @@ spec: More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it properties: name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -1288,6 +1349,8 @@ spec: cinder: description: |- cinder represents a cinder volume attached and mounted on kubelets host machine. + Deprecated: Cinder is deprecated. All operations for the in-tree cinder type + are redirected to the cinder.csi.openstack.org CSI driver. More info: https://examples.k8s.io/mysql-cinder-pd/README.md properties: fsType: @@ -1309,10 +1372,13 @@ spec: to OpenStack. properties: name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -1377,11 +1443,15 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: optional specify whether the ConfigMap @@ -1392,7 +1462,7 @@ spec: csi: description: csi (Container Storage Interface) represents ephemeral storage that is handled by certain external - CSI drivers (Beta feature). + CSI drivers. properties: driver: description: |- @@ -1414,10 +1484,13 @@ spec: secret object contains more than one secret, all secret references are passed. properties: name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -1461,8 +1534,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + pod: only annotations, labels, name, namespace + and uid are supported.' properties: apiVersion: description: Version of the schema the FieldPath @@ -1521,6 +1594,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: description: |- @@ -1554,7 +1628,6 @@ spec: The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, and deleted when the pod is removed. - Use this if: a) the volume is only needed while the pod runs, b) features of normal volumes like restoring from snapshot or capacity @@ -1565,17 +1638,14 @@ spec: information on the connection between this volume type and PersistentVolumeClaim). - Use PersistentVolumeClaim or one of the vendor-specific APIs for volumes that persist for longer than the lifecycle of an individual pod. - Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to be used that way - see the documentation of the driver for more information. - A pod can use both types of ephemeral volumes and persistent volumes at the same time. properties: @@ -1589,7 +1659,6 @@ spec: entry. Pod validation will reject the pod if the concatenated name is not valid for a PVC (for example, too long). - An existing PVC with that name that is not owned by the pod will *not* be used for the pod to avoid using an unrelated volume by mistake. Starting the pod is then blocked until @@ -1599,11 +1668,9 @@ spec: this should not be necessary, but it may be useful when manually reconstructing a broken cluster. - This field is read-only and no changes will be made by Kubernetes to the PVC after it has been created. - Required, must not be nil. properties: metadata: @@ -1626,6 +1693,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -1714,34 +1782,6 @@ spec: status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources properties: - claims: - description: |- - Claims lists the names of resources, defined in spec.resourceClaims, - that are used by this container. - - - This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. - - - This field is immutable. It can only be set for containers. - items: - description: ResourceClaim references - one entry in PodSpec.ResourceClaims. - properties: - name: - description: |- - Name must match the name of one entry in pod.spec.resourceClaims of - the Pod where this field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1798,11 +1838,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -1818,6 +1860,21 @@ spec: storageClassName is the name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ + (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default). + type: string volumeMode: description: |- volumeMode defines what type of volume is required by the claim. @@ -1842,7 +1899,6 @@ spec: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - TODO: how do we prevent errors in the filesystem from compromising the machine type: string lun: description: 'lun is Optional: FC target lun number' @@ -1859,6 +1915,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: description: |- wwids Optional: FC volume world wide identifiers (wwids) @@ -1866,11 +1923,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: description: |- flexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin. + Deprecated: FlexVolume is deprecated. Consider using a CSIDriver instead. properties: driver: description: driver is the name of the driver to use @@ -1902,10 +1961,13 @@ spec: scripts. properties: name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -1913,9 +1975,9 @@ spec: - driver type: object flocker: - description: flocker represents a Flocker volume attached - to a kubelet's host machine. This depends on the Flocker - control service being running + description: |- + flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running. + Deprecated: Flocker is deprecated and the in-tree flocker type is no longer supported. properties: datasetName: description: |- @@ -1931,6 +1993,8 @@ spec: description: |- gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. + Deprecated: GCEPersistentDisk is deprecated. All operations for the in-tree + gcePersistentDisk type are redirected to the pd.csi.storage.gke.io CSI driver. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk properties: fsType: @@ -1939,7 +2003,6 @@ spec: Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from compromising the machine type: string partition: description: |- @@ -1967,7 +2030,7 @@ spec: gitRepo: description: |- gitRepo represents a git repository at a particular revision. - DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + Deprecated: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container. properties: @@ -1991,6 +2054,7 @@ spec: glusterfs: description: |- glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported. More info: https://examples.k8s.io/volumes/glusterfs/README.md properties: endpoints: @@ -2020,9 +2084,6 @@ spec: used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- - TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not - mount host directories as read/write. properties: path: description: |- @@ -2039,6 +2100,41 @@ spec: required: - path type: object + image: + description: |- + image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. + The volume is resolved at pod startup depending on which PullPolicy value is provided: + + - Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + + The volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. + A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. + The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. + The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. + The volume will be mounted read-only (ro) and non-executable files (noexec). + Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath). + The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type. + properties: + pullPolicy: + description: |- + Policy for pulling OCI objects. Possible values are: + Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + type: string + reference: + description: |- + Required: Image or artifact reference to be used. + Behaves in the same way as pod.spec.containers[*].image. + Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + type: object iscsi: description: |- iscsi represents an ISCSI Disk resource that is attached to a @@ -2059,7 +2155,6 @@ spec: Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from compromising the machine type: string initiatorName: description: |- @@ -2071,6 +2166,7 @@ spec: description: iqn is the target iSCSI Qualified Name. type: string iscsiInterface: + default: default description: |- iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp). @@ -2086,6 +2182,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: description: |- readOnly here will force the ReadOnly setting in VolumeMounts. @@ -2096,10 +2193,13 @@ spec: target and initiator authentication properties: name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -2164,9 +2264,9 @@ spec: - claimName type: object photonPersistentDisk: - description: photonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets host - machine + description: |- + photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine. + Deprecated: PhotonPersistentDisk is deprecated and the in-tree photonPersistentDisk type is no longer supported. properties: fsType: description: |- @@ -2182,8 +2282,11 @@ spec: - pdID type: object portworxVolume: - description: portworxVolume represents a portworx volume - attached and mounted on kubelets host machine + description: |- + portworxVolume represents a portworx volume attached and mounted on kubelets host machine. + Deprecated: PortworxVolume is deprecated. All operations for the in-tree portworxVolume type + are redirected to the pxd.portworx.com CSI driver when the CSIMigrationPortworx feature-gate + is on. properties: fsType: description: |- @@ -2218,11 +2321,107 @@ spec: format: int32 type: integer sources: - description: sources is the list of volume projections + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. items: - description: Projection that may be projected along - with other supported volume types + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume + root to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object configMap: description: configMap information about the configMap data to project @@ -2265,11 +2464,15 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: optional specify whether the @@ -2292,7 +2495,7 @@ spec: fieldRef: description: 'Required: Selects a field of the pod: only annotations, labels, - name and namespace are supported.' + name, namespace and uid are supported.' properties: apiVersion: description: Version of the schema @@ -2356,6 +2559,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the secret @@ -2399,11 +2603,15 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: optional field specify whether @@ -2442,10 +2650,12 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: - description: quobyte represents a Quobyte mount on the host - that shares a pod's lifetime + description: |- + quobyte represents a Quobyte mount on the host that shares a pod's lifetime. + Deprecated: Quobyte is deprecated and the in-tree quobyte type is no longer supported. properties: group: description: |- @@ -2484,6 +2694,7 @@ spec: rbd: description: |- rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported. More info: https://examples.k8s.io/volumes/rbd/README.md properties: fsType: @@ -2492,7 +2703,6 @@ spec: Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from compromising the machine type: string image: description: |- @@ -2500,6 +2710,7 @@ spec: More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it type: string keyring: + default: /etc/ceph/keyring description: |- keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. @@ -2512,7 +2723,9 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic pool: + default: rbd description: |- pool is the rados pool name. Default is rbd. @@ -2532,14 +2745,18 @@ spec: More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it properties: name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic user: + default: admin description: |- user is the rados user name. Default is admin. @@ -2550,10 +2767,12 @@ spec: - monitors type: object scaleIO: - description: scaleIO represents a ScaleIO persistent volume - attached and mounted on Kubernetes nodes. + description: |- + scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes. + Deprecated: ScaleIO is deprecated and the in-tree scaleIO type is no longer supported. properties: fsType: + default: xfs description: |- fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. @@ -2579,10 +2798,13 @@ spec: sensitive information. If this is not provided, Login operation will fail. properties: name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -2591,6 +2813,7 @@ spec: with Gateway, default false type: boolean storageMode: + default: ThinProvisioned description: |- storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. @@ -2667,6 +2890,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: description: optional field specify whether the Secret or its keys must be defined @@ -2678,8 +2902,9 @@ spec: type: string type: object storageos: - description: storageOS represents a StorageOS volume attached - and mounted on Kubernetes nodes. + description: |- + storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes. + Deprecated: StorageOS is deprecated and the in-tree storageos type is no longer supported. properties: fsType: description: |- @@ -2698,10 +2923,13 @@ spec: credentials. If not specified, default values will be attempted. properties: name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -2721,8 +2949,10 @@ spec: type: string type: object vsphereVolume: - description: vsphereVolume represents a vSphere volume attached - and mounted on kubelets host machine + description: |- + vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine. + Deprecated: VsphereVolume is deprecated. All operations for the in-tree vsphereVolume type + are redirected to the csi.vsphere.vmware.com CSI driver. properties: fsType: description: |- @@ -2757,44 +2987,6 @@ spec: type: string network: properties: - extraPorts: - items: - description: ContainerPort represents a network port in a single - container. - properties: - containerPort: - description: |- - Number of port to expose on the pod's IP address. - This must be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external port to. - type: string - hostPort: - description: |- - Number of port to expose on the host. - If specified, this must be a valid port number, 0 < x < 65536. - If HostNetwork is specified, this must match ContainerPort. - Most containers do not need this. - format: int32 - type: integer - name: - description: |- - If specified, this must be an IANA_SVC_NAME and unique within the pod. Each - named port in a pod must have a unique name. Name for the port that can be - referred to by services. - type: string - protocol: - default: TCP - description: |- - Protocol for port. Must be UDP, TCP, or SCTP. - Defaults to "TCP". - type: string - required: - - containerPort - type: object - type: array type: enum: - NodePort @@ -2822,11 +3014,16 @@ spec: default: false type: boolean state: + default: Running enum: - Running - Stopped - Shutdown type: string + storageLimit: + description: devbox storage limit, `storageLimit` will be used to + generate the devbox pod label. + type: string templateID: type: string tolerations: @@ -2870,112 +3067,58 @@ spec: required: - config - image + - network - resource - state type: object status: description: DevboxStatus defines the observed state of Devbox properties: - commitHistory: - items: + commitRecords: + additionalProperties: properties: - containerID: - description: ContainerID is the container id + baseImage: + description: BaseImage is the image of the that devbox is running + on type: string - image: - description: Image is the image of the commit + commitImage: + description: CommitImage is the image of the that devbox is + committed to type: string - node: - description: Node is the node name + commitStatus: + default: Pending + description: CommitStatus is the status of the commit + enum: + - Success + - Failed + - Pending type: string - pod: - description: Pod is the pod name + commitTime: + description: CommitTime is the time when the commit is created + format: date-time type: string - predicatedStatus: - description: predicatedStatus default `pending`, will be set - to `success` if pod status is running successfully. + generateTime: + description: GenerateTime is the time when the commit is generated + format: date-time type: string - status: - description: status will be set based on expectedStatus after - devbox pod delete or stop. if expectedStatus is still pending, - it means the pod is not running successfully, so we need to - set it to `failed` + node: + description: Node is the node name + type: string + scheduleTime: + description: ScheduleTime is the time when the commit is scheduled + format: date-time type: string - time: - description: Time is the time when the commit is created + updateTime: + description: UpdateTime is the time when the commit is updated format: date-time type: string required: - - containerID - - image - - node - - pod - - predicatedStatus - - status - - time + - commitStatus type: object - type: array - lastState: - description: |- - ContainerState holds a possible state of container. - Only one of its members may be specified. - If none of them is specified, the default one is ContainerStateWaiting. - properties: - running: - description: Details about a running container - properties: - startedAt: - description: Time at which the container was last (re-)started - format: date-time - type: string - type: object - terminated: - description: Details about a terminated container - properties: - containerID: - description: Container's ID in the format '://' - type: string - exitCode: - description: Exit status from the last termination of the - container - format: int32 - type: integer - finishedAt: - description: Time at which the container last terminated - format: date-time - type: string - message: - description: Message regarding the last termination of the - container - type: string - reason: - description: (brief) reason from the last termination of the - container - type: string - signal: - description: Signal from the last termination of the container - format: int32 - type: integer - startedAt: - description: Time at which previous execution of the container - started - format: date-time - type: string - required: - - exitCode - type: object - waiting: - description: Details about a waiting container - properties: - message: - description: Message regarding why the container is not yet - running. - type: string - reason: - description: (brief) reason the container is not yet running. - type: string - type: object + description: CommitRecords is the records of the devbox commits type: object + contentID: + type: string network: properties: nodePort: @@ -2996,66 +3139,10 @@ spec: phase: type: string state: - description: |- - ContainerState holds a possible state of container. - Only one of its members may be specified. - If none of them is specified, the default one is ContainerStateWaiting. - properties: - running: - description: Details about a running container - properties: - startedAt: - description: Time at which the container was last (re-)started - format: date-time - type: string - type: object - terminated: - description: Details about a terminated container - properties: - containerID: - description: Container's ID in the format '://' - type: string - exitCode: - description: Exit status from the last termination of the - container - format: int32 - type: integer - finishedAt: - description: Time at which the container last terminated - format: date-time - type: string - message: - description: Message regarding the last termination of the - container - type: string - reason: - description: (brief) reason from the last termination of the - container - type: string - signal: - description: Signal from the last termination of the container - format: int32 - type: integer - startedAt: - description: Time at which previous execution of the container - started - format: date-time - type: string - required: - - exitCode - type: object - waiting: - description: Details about a waiting container - properties: - message: - description: Message regarding why the container is not yet - running. - type: string - reason: - description: (brief) reason the container is not yet running. - type: string - type: object - type: object + default: Running + type: string + required: + - commitRecords type: object type: object served: true diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml deleted file mode 100644 index 6ef4ad257b48..000000000000 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: devboxreleases.devbox.sealos.io -spec: - group: devbox.sealos.io - names: - kind: DevBoxRelease - listKind: DevBoxReleaseList - plural: devboxreleases - singular: devboxrelease - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.devboxName - name: DevboxName - type: string - - jsonPath: .spec.newTag - name: NewTag - type: string - - jsonPath: .status.phase - name: Phase - type: string - - jsonPath: .status.originalImage - name: OriginalImage - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: DevBoxRelease is the Schema for the devboxreleases 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 - 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 - type: string - metadata: - type: object - spec: - description: DevBoxReleaseSpec defines the desired state of DevBoxRelease - properties: - devboxName: - type: string - newTag: - type: string - notes: - type: string - required: - - devboxName - - newTag - type: object - status: - description: DevBoxReleaseStatus defines the observed state of DevBoxRelease - properties: - originalImage: - type: string - phase: - default: Pending - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_operationrequests.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_operationrequests.yaml deleted file mode 100644 index df5f0d040115..000000000000 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_operationrequests.yaml +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: operationrequests.devbox.sealos.io -spec: - group: devbox.sealos.io - names: - kind: OperationRequest - listKind: OperationRequestList - plural: operationrequests - singular: operationrequest - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: OperationRequest is the Schema for the operationrequests 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 - 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 - type: string - metadata: - type: object - spec: - description: OperationRequestSpec defines the desired state of OperationRequest - type: object - status: - description: OperationRequestStatus defines the observed state of OperationRequest - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/controllers/devbox/config/crd/kustomization.yaml b/controllers/devbox/config/crd/kustomization.yaml index 37bb4dbbdd96..26fcd276a675 100644 --- a/controllers/devbox/config/crd/kustomization.yaml +++ b/controllers/devbox/config/crd/kustomization.yaml @@ -17,8 +17,6 @@ # It should be run by config/default resources: - bases/devbox.sealos.io_devboxes.yaml -- bases/devbox.sealos.io_devboxreleases.yaml -- bases/devbox.sealos.io_operationrequests.yaml # +kubebuilder:scaffold:crdkustomizeresource patches: @@ -29,8 +27,6 @@ patches: # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- path: patches/cainjection_in_devboxes.yaml -#- path: patches/cainjection_in_devboxreleases.yaml -#- path: patches/cainjection_in_operationrequests.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # [WEBHOOK] To enable webhook, uncomment the following section diff --git a/controllers/devbox/config/rbac/devboxrelease_editor_role.yaml b/controllers/devbox/config/rbac/devboxrelease_editor_role.yaml deleted file mode 100644 index 413902bb14f1..000000000000 --- a/controllers/devbox/config/rbac/devboxrelease_editor_role.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# permissions for end users to edit devboxreleases. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: devboxrelease-editor-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - devboxreleases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - devbox.sealos.io - resources: - - devboxreleases/status - verbs: - - get diff --git a/controllers/devbox/config/rbac/devboxrelease_viewer_role.yaml b/controllers/devbox/config/rbac/devboxrelease_viewer_role.yaml deleted file mode 100644 index 5820092d6c74..000000000000 --- a/controllers/devbox/config/rbac/devboxrelease_viewer_role.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# permissions for end users to view devboxreleases. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: devboxrelease-viewer-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - devboxreleases - verbs: - - get - - list - - watch -- apiGroups: - - devbox.sealos.io - resources: - - devboxreleases/status - verbs: - - get diff --git a/controllers/devbox/config/rbac/kustomization.yaml b/controllers/devbox/config/rbac/kustomization.yaml index 1a77a2ef46e5..d092c10c0104 100644 --- a/controllers/devbox/config/rbac/kustomization.yaml +++ b/controllers/devbox/config/rbac/kustomization.yaml @@ -36,10 +36,5 @@ resources: # default, aiding admins in cluster management. Those roles are # not used by the Project itself. You can comment the following lines # if you do not want those helpers be installed with your Project. -- operationrequest_editor_role.yaml -- operationrequest_viewer_role.yaml -- devboxrelease_editor_role.yaml -- devboxrelease_viewer_role.yaml - devbox_editor_role.yaml - devbox_viewer_role.yaml - diff --git a/controllers/devbox/config/rbac/operationrequest_editor_role.yaml b/controllers/devbox/config/rbac/operationrequest_editor_role.yaml deleted file mode 100644 index eacf84b90c76..000000000000 --- a/controllers/devbox/config/rbac/operationrequest_editor_role.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# permissions for end users to edit operationrequests. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: operationrequest-editor-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - operationrequests - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - devbox.sealos.io - resources: - - operationrequests/status - verbs: - - get diff --git a/controllers/devbox/config/rbac/operationrequest_viewer_role.yaml b/controllers/devbox/config/rbac/operationrequest_viewer_role.yaml deleted file mode 100644 index 39f6e842af5e..000000000000 --- a/controllers/devbox/config/rbac/operationrequest_viewer_role.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# permissions for end users to view operationrequests. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: operationrequest-viewer-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - operationrequests - verbs: - - get - - list - - watch -- apiGroups: - - devbox.sealos.io - resources: - - operationrequests/status - verbs: - - get diff --git a/controllers/devbox/config/rbac/role.yaml b/controllers/devbox/config/rbac/role.yaml index c7916fa8cbdc..5d76ec1c5344 100644 --- a/controllers/devbox/config/rbac/role.yaml +++ b/controllers/devbox/config/rbac/role.yaml @@ -1,17 +1,3 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -22,12 +8,9 @@ rules: - "" resources: - events - verbs: - - '*' -- apiGroups: - - "" - resources: - pods + - secrets + - services verbs: - '*' - apiGroups: @@ -38,22 +21,12 @@ rules: - get - patch - update -- apiGroups: - - "" - resources: - - secrets - verbs: - - '*' -- apiGroups: - - "" - resources: - - services - verbs: - - '*' - apiGroups: - devbox.sealos.io resources: - devboxes + - runtimeclasses + - runtimes verbs: - create - delete @@ -76,53 +49,3 @@ rules: - get - patch - update -- apiGroups: - - devbox.sealos.io - resources: - - devboxreleases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - devbox.sealos.io - resources: - - devboxreleases/finalizers - verbs: - - update -- apiGroups: - - devbox.sealos.io - resources: - - devboxreleases/status - verbs: - - get - - patch - - update -- apiGroups: - - devbox.sealos.io - resources: - - runtimeclasses - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - devbox.sealos.io - resources: - - runtimes - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml b/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml index 71e5bf070b02..ca0938eb745c 100644 --- a/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml +++ b/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml @@ -15,26 +15,13 @@ apiVersion: devbox.sealos.io/v1alpha1 kind: Devbox metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: devbox-gpu-sample + name: devbox-sample spec: state: Running - runtimeClassName: nvidia + image: busybox:latest + config: {} resource: cpu: 2 memory: 4000Mi - nvidia.com/gpu: 1 - runtimeRef: - name: go-1-22-5-2024-11-12-0651 - namespace: devbox-system - nodeSelector: - nvidia.com/gpu.product: Tesla-P40 network: type: NodePort - extraPorts: - - containerPort: 443 - name: 'https' - - containerPort: 80 - name: 'http' diff --git a/controllers/devbox/config/samples/kustomization.yaml b/controllers/devbox/config/samples/kustomization.yaml index 2eac70ab48a5..26ee8c0257f2 100644 --- a/controllers/devbox/config/samples/kustomization.yaml +++ b/controllers/devbox/config/samples/kustomization.yaml @@ -15,8 +15,4 @@ ## Append samples of your project ## resources: - devbox_v1alpha1_devbox.yaml -- devbox_v1alpha1_runtime.yaml -- devbox_v1alpha1_runtimeclass.yaml -- devbox_v1alpha1_devboxrelease.yaml -- devbox_v1alpha1_operationrequest.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/controllers/devbox/config/samples/test/devbox_v1alpha1_devbox.yaml b/controllers/devbox/config/samples/test/devbox_v1alpha1_devbox.yaml index e5de2960de68..7a88714a0529 100644 --- a/controllers/devbox/config/samples/test/devbox_v1alpha1_devbox.yaml +++ b/controllers/devbox/config/samples/test/devbox_v1alpha1_devbox.yaml @@ -15,30 +15,15 @@ apiVersion: devbox.sealos.io/v1alpha1 kind: Devbox metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: devbox-sample + name: devbox-sample-1 spec: - state: Running - resource: - cpu: 1000m - memory: 1024Mi - runtimeRef: - name: go-1-22-5 - namespace: devbox-system - command: - - /bin/bash - args: - - -c - - | - apt-get update - apt-get install -y tini - tini -- /bin/bash -c "echo 'DevBox is ready for testing'; tail -f /dev/null" + squash: false network: type: NodePort - extraPorts: - - containerPort: 443 - name: 'https' - - containerPort: 80 - name: 'http' + runtimeClassName: devbox-runtime + resource: + cpu: 2000m + memory: 4096Mi + image: ghcr.io/labring-actions/devbox/go-1.23.0:13aacd8 + config: + workingDir: /home/devbox/project diff --git a/controllers/devbox/go.mod b/controllers/devbox/go.mod index 96aec916fd01..58d1e7521e87 100644 --- a/controllers/devbox/go.mod +++ b/controllers/devbox/go.mod @@ -3,10 +3,10 @@ module github.com/labring/sealos/controllers/devbox go 1.24.0 require ( - github.com/google/go-containerregistry v0.20.2 + github.com/google/uuid v1.6.0 github.com/onsi/ginkgo/v2 v2.22.0 github.com/onsi/gomega v1.36.1 - golang.org/x/crypto v0.28.0 + golang.org/x/crypto v0.31.0 k8s.io/api v0.32.1 k8s.io/apimachinery v0.32.1 k8s.io/client-go v0.32.1 @@ -43,7 +43,6 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect - github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -52,7 +51,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 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/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect @@ -73,19 +71,19 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/term v0.25.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.26.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.35.1 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/controllers/devbox/go.sum b/controllers/devbox/go.sum index d9c392bd031c..7018ef5deb76 100644 --- a/controllers/devbox/go.sum +++ b/controllers/devbox/go.sum @@ -60,8 +60,6 @@ github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYu github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= -github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -99,8 +97,6 @@ github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -161,8 +157,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -171,26 +167,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -211,8 +207,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go new file mode 100644 index 000000000000..691e09bacec4 --- /dev/null +++ b/controllers/devbox/internal/commit/commit.go @@ -0,0 +1,26 @@ +package commit + +import ( + "context" + "fmt" +) + +type Committer interface { + Commit(ctx context.Context, devboxName string, contentID string, baseImage string, commitImage string) error +} + +type CommitterImpl struct { + // Client *containerd.Client +} + +func (c *CommitterImpl) Commit(ctx context.Context, devboxName string, contentID string, baseImage string, commitImage string) error { + fmt.Println("========>>>> commit devbox", devboxName, contentID, baseImage, commitImage) + return nil +} + +type GcHandler interface { + GC(ctx context.Context) error +} + +type Handler struct { +} diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 6e2e14d16502..d9c79db231d5 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -27,6 +27,7 @@ import ( "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" "github.com/labring/sealos/controllers/devbox/label" + "github.com/google/uuid" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -50,6 +51,8 @@ import ( // DevboxReconciler reconciles a Devbox object type DevboxReconciler struct { CommitImageRegistry string + DevboxNodeLabel string + NodeName string RequestRate resource.RequestRate EphemeralStorage resource.EphemeralStorage @@ -59,8 +62,10 @@ type DevboxReconciler struct { DebugMode bool client.Client - Scheme *runtime.Scheme - Recorder record.EventRecorder + Scheme *runtime.Scheme + Recorder record.EventRecorder + StateChangeRecorder record.EventRecorder + RestartPredicateDuration time.Duration } @@ -86,7 +91,7 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr recLabels := label.RecommendedLabels(&label.Recommended{ Name: devbox.Name, ManagedBy: label.DefaultManagedBy, - PartOf: devboxv1alpha1.DevBoxPartOf, + PartOf: devboxv1alpha1.LabelDevBoxPartOf, }) if devbox.ObjectMeta.DeletionTimestamp.IsZero() { @@ -118,9 +123,43 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } return ctrl.Result{}, nil } - + // init devbox status network type devbox.Status.Network.Type = devbox.Spec.NetworkSpec.Type - _ = r.Status().Update(ctx, devbox) + // init devbox status content id + if devbox.Status.ContentID == "" { + devbox.Status.ContentID = uuid.New().String() + } + // init devbox status commit record + if devbox.Status.CommitRecords[devbox.Status.ContentID] == nil { + devbox.Status.CommitRecords = make(map[string]*devboxv1alpha1.CommitRecord) + devbox.Status.State = devboxv1alpha1.DevboxStateRunning + devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha1.CommitRecord{ + Node: "", + BaseImage: devbox.Spec.Image, + CommitImage: r.generateImageName(devbox), + CommitStatus: devboxv1alpha1.CommitStatusPending, + GenerateTime: metav1.Now(), + } + } + + // todo: implement the schedule logic to replace the current logic + // if devbox state is running, schedule devbox to node, update devbox status and create a new commit record + // and filter out the devbox that are not in the current node + if devbox.Spec.State == devboxv1alpha1.DevboxStateRunning { + if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == "" { + // set up devbox node and content id, new a record for the devbox + devbox.Status.CommitRecords[devbox.Status.ContentID].Node = r.NodeName + if err := r.Status().Update(ctx, devbox); err != nil { + logger.Info("try to schedule devbox to node failed", "error", err) + return ctrl.Result{}, nil + } + logger.Info("devbox scheduled to node", "node", r.NodeName) + r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox scheduled to node", "Devbox scheduled to node") + } else if devbox.Status.CommitRecords[devbox.Status.ContentID].Node != r.NodeName { + logger.Info("devbox already scheduled to node", "node", devbox.Status.CommitRecords[devbox.Status.ContentID].Node) + return ctrl.Result{}, nil + } + } // create or update secret logger.Info("syncing secret") @@ -131,7 +170,6 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } logger.Info("sync secret success") r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Sync secret success", "Sync secret success") - // create service if network type is NodePort if devbox.Spec.NetworkSpec.Type == devboxv1alpha1.NetworkTypeNodePort { logger.Info("syncing service") @@ -147,6 +185,15 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Sync service success", "Sync service success") } + // sync devbox state + logger.Info("syncing devbox state") + if stateChanged := r.syncDevboxState(ctx, devbox); stateChanged { + logger.Info("devbox state changed, wait for state change handler to handle the event, requeue after 5 seconds", "from", devbox.Status.State, "to", devbox.Spec.State) + r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) + return ctrl.Result{RequeueAfter: 5 * time.Second}, nil + } + logger.Info("sync devbox state success") + // create or update pod logger.Info("syncing pod") if err := r.syncPod(ctx, devbox, recLabels); err != nil { @@ -225,142 +272,65 @@ func (r *DevboxReconciler) syncSecret(ctx context.Context, devbox *devboxv1alpha func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error { logger := log.FromContext(ctx) - - var podList corev1.PodList - if err := r.List(ctx, &podList, client.InNamespace(devbox.Namespace), client.MatchingLabels(recLabels)); err != nil { + podList := &corev1.PodList{} + if err := r.List(ctx, podList, client.InNamespace(devbox.Namespace), client.MatchingLabels(recLabels)); err != nil { return err } - // only one pod is allowed, if more than one pod found, return error - if len(podList.Items) > 1 { - // remove finalizer and delete them - for _, pod := range podList.Items { - if controllerutil.RemoveFinalizer(&pod, devboxv1alpha1.FinalizerName) { - if err := r.Update(ctx, &pod); err != nil { - logger.Error(err, "remove finalizer failed") - } - } - if err := r.Delete(ctx, &pod); err != nil { - logger.Error(err, "delete pod failed") - } - } - return fmt.Errorf("more than one pod found") - } - logger.Info("pod list", "length", len(podList.Items)) - - // update devbox status after pod is created or updated - defer func() { - if err := retry.RetryOnConflict(retry.DefaultRetry, func() error { - logger.Info("update devbox status after pod synced") - latestDevbox := &devboxv1alpha1.Devbox{} - if err := r.Client.Get(ctx, client.ObjectKey{Namespace: devbox.Namespace, Name: devbox.Name}, latestDevbox); err != nil { - logger.Error(err, "get latest devbox failed") - return err - } - // update devbox status with latestDevbox status - logger.Info("updating devbox status") - logger.Info("merge commit history", "devbox", devbox.Status.CommitHistory, "latestDevbox", latestDevbox.Status.CommitHistory) - devbox.Status.Phase = helper.GenerateDevboxPhase(devbox, podList) - helper.UpdateDevboxStatus(devbox, latestDevbox) - return r.Status().Update(ctx, latestDevbox) - }); err != nil { - logger.Error(err, "sync pod failed") - r.Recorder.Eventf(devbox, corev1.EventTypeWarning, "Sync pod failed", "%v", err) - return - } - logger.Info("update devbox status success") - r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Sync pod success", "Sync pod success") - }() - switch devbox.Spec.State { case devboxv1alpha1.DevboxStateRunning: - nextCommitHistory := r.generateNextCommitHistory(devbox) - expectPod := r.generateDevboxPod(devbox, nextCommitHistory) - + // get pod switch len(podList.Items) { case 0: - logger.Info("create pod") - logger.Info("next commit history", "commit", nextCommitHistory) - err := r.createPod(ctx, devbox, expectPod, nextCommitHistory) - if err != nil && helper.IsExceededQuotaError(err) { - logger.Info("devbox is exceeded quota, change devbox state to Stopped") - r.Recorder.Eventf(devbox, corev1.EventTypeWarning, "Devbox is exceeded quota", "Devbox is exceeded quota") - devbox.Spec.State = devboxv1alpha1.DevboxStateStopped - _ = r.Update(ctx, devbox) - return nil + // check last devbox status + currentRecord := devbox.Status.CommitRecords[devbox.Status.ContentID] + if currentRecord == nil { + return fmt.Errorf("current record is nil") } - if err != nil { - logger.Error(err, "create pod failed") + // create a new pod with default image, with new content id + pod := r.generateDevboxPod(devbox, + helper.WithPodImage(currentRecord.BaseImage), + helper.WithPodContentID(devbox.Status.ContentID), + helper.WithPodNodeName(currentRecord.Node), + ) + if err := r.Create(ctx, pod); err != nil { return err } - return nil case 1: - pod := &podList.Items[0] - // check pod container size, if it is 0, it means the pod is not running, return an error - if len(pod.Status.ContainerStatuses) == 0 { - return fmt.Errorf("pod container size is 0") - } - devbox.Status.State = pod.Status.ContainerStatuses[0].State - // update commit predicated status by pod status, this should be done once find a pod - helper.UpdatePredicatedCommitStatus(devbox, pod) - // pod has been deleted, handle it, next reconcile will create a new pod, and we will update commit history status by predicated status - if !pod.DeletionTimestamp.IsZero() { - logger.Info("pod has been deleted") - return r.handlePodDeleted(ctx, devbox, pod) + // skip if pod is already created + if !podList.Items[0].DeletionTimestamp.IsZero() { + return r.handlePodDeleted(ctx, &podList.Items[0]) } - switch matcher.PodMatchExpectations(expectPod, pod, r.PodMatchers...) { - case true: - // pod match expectations - logger.Info("pod match expectations") - switch pod.Status.Phase { - case corev1.PodPending, corev1.PodRunning: - // pod is running or pending, do nothing here - logger.Info("pod is running or pending") - // update commit history status by pod status - helper.UpdateCommitHistory(devbox, pod, false) - return nil - case corev1.PodFailed, corev1.PodSucceeded: - // pod failed or succeeded, we need delete pod and remove finalizer - logger.Info("pod failed or succeeded, recreate pod") - return r.deletePod(ctx, devbox, pod) - } - case false: - // pod not match expectations, delete pod anyway - logger.Info("pod not match expectations, recreate pod") - return r.deletePod(ctx, devbox, pod) + return nil + default: + // more than one pod found, remove finalizer and delete them + for _, pod := range podList.Items { + r.deletePod(ctx, &pod) } + logger.Error(fmt.Errorf("more than one pod found"), "more than one pod found") + r.Recorder.Eventf(devbox, corev1.EventTypeWarning, "More than one pod found", "More than one pod found") + return fmt.Errorf("more than one pod found") } case devboxv1alpha1.DevboxStateStopped, devboxv1alpha1.DevboxStateShutdown: - switch len(podList.Items) { - case 0: - return nil - case 1: - pod := &podList.Items[0] - // update state to empty since devbox is stopped - devbox.Status.State = corev1.ContainerState{} - // update commit predicated status by pod status, this should be done once find a pod - helper.UpdatePredicatedCommitStatus(devbox, pod) - // pod has been deleted, handle it, next reconcile will create a new pod, and we will update commit history status by predicated status - if !pod.DeletionTimestamp.IsZero() { - return r.handlePodDeleted(ctx, devbox, pod) + if len(podList.Items) > 0 { + for _, pod := range podList.Items { + r.deletePod(ctx, &pod) } - // we need delete pod because devbox state is stopped - // we don't care about the pod status, just delete it - return r.deletePod(ctx, devbox, pod) } + return nil } return nil } func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error { var servicePorts []corev1.ServicePort - for _, port := range devbox.Spec.Config.Ports { - servicePorts = append(servicePorts, corev1.ServicePort{ - Name: port.Name, - Port: port.ContainerPort, - TargetPort: intstr.FromInt32(port.ContainerPort), - Protocol: port.Protocol, - }) - } + // for _, port := range devbox.Spec.Config.Ports { + // servicePorts = append(servicePorts, corev1.ServicePort{ + // Name: port.Name, + // Port: port.ContainerPort, + // TargetPort: intstr.FromInt32(port.ContainerPort), + // Protocol: port.Protocol, + // }) + // } if len(servicePorts) == 0 { //use the default value servicePorts = []corev1.ServicePort{ @@ -442,32 +412,33 @@ func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alph return nil } -// create a new pod, add predicated status to nextCommitHistory -func (r *DevboxReconciler) createPod(ctx context.Context, devbox *devboxv1alpha1.Devbox, expectPod *corev1.Pod, nextCommitHistory *devboxv1alpha1.CommitHistory) error { +// sync devbox state, and record the state change event to state change recorder, state change handler will handle the event +func (r *DevboxReconciler) syncDevboxState(ctx context.Context, devbox *devboxv1alpha1.Devbox) bool { logger := log.FromContext(ctx) - - logger.Info("creating pod", - "podName", expectPod.Name, - "namespace", expectPod.Namespace, - "nextCommitHistory", nextCommitHistory) - - nextCommitHistory.Status = devboxv1alpha1.CommitStatusPending - nextCommitHistory.PredicatedStatus = devboxv1alpha1.CommitStatusPending - - if expectPod.Name == "" { - return fmt.Errorf("pod name cannot be empty") - } - - if err := r.Create(ctx, expectPod); err != nil { - logger.Error(err, "failed to create pod") - return err - } - - devbox.Status.CommitHistory = append(devbox.Status.CommitHistory, nextCommitHistory) - return nil + logger.Info("syncDevboxState called", + "devbox", devbox.Name, + "specState", devbox.Spec.State, + "statusState", devbox.Status.State, + "nodeName", r.NodeName) + + if devbox.Spec.State != devbox.Status.State { + logger.Info("devbox state changing", + "from", devbox.Status.State, + "to", devbox.Spec.State, + "devbox", devbox.Name) + logger.Info("recording state change event", + "devbox", devbox.Name, + "nodeName", r.NodeName) + r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) + return true + } + logger.Info("devbox state unchanged", + "devbox", devbox.Name, + "state", devbox.Spec.State) + return false } -func (r *DevboxReconciler) deletePod(ctx context.Context, devbox *devboxv1alpha1.Devbox, pod *corev1.Pod) error { +func (r *DevboxReconciler) deletePod(ctx context.Context, pod *corev1.Pod) error { logger := log.FromContext(ctx) // remove finalizer and delete pod controllerutil.RemoveFinalizer(pod, devboxv1alpha1.FinalizerName) @@ -479,29 +450,24 @@ func (r *DevboxReconciler) deletePod(ctx context.Context, devbox *devboxv1alpha1 logger.Error(err, "delete pod failed") return err } - // update commit history status because pod has been deleted - if len(pod.Status.ContainerStatuses) != 0 { - devbox.Status.LastTerminationState = pod.Status.ContainerStatuses[0].State - } - helper.UpdateCommitHistory(devbox, pod, true) return nil } -func (r *DevboxReconciler) handlePodDeleted(ctx context.Context, devbox *devboxv1alpha1.Devbox, pod *corev1.Pod) error { +func (r *DevboxReconciler) handlePodDeleted(ctx context.Context, pod *corev1.Pod) error { logger := log.FromContext(ctx) controllerutil.RemoveFinalizer(pod, devboxv1alpha1.FinalizerName) if err := r.Update(ctx, pod); err != nil { logger.Error(err, "remove finalizer failed") return err } - // update commit history status because pod has been deleted - if len(pod.Status.ContainerStatuses) != 0 { - devbox.Status.LastTerminationState = pod.Status.ContainerStatuses[0].State - } - helper.UpdateCommitHistory(devbox, pod, true) return nil } +func (r *DevboxReconciler) generateImageName(devbox *devboxv1alpha1.Devbox) string { + now := time.Now() + return fmt.Sprintf("%s/%s/%s:%s-%s", r.CommitImageRegistry, devbox.Namespace, devbox.Name, rand.String(5), now.Format("2006-01-02-150405")) +} + func (r *DevboxReconciler) removeAll(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error { // Delete Pod podList := &corev1.PodList{} @@ -534,28 +500,19 @@ func (r *DevboxReconciler) deleteResourcesByLabels(ctx context.Context, obj clie return client.IgnoreNotFound(err) } -func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, nextCommitHistory *devboxv1alpha1.CommitHistory) *corev1.Pod { +func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, opts ...helper.DevboxPodOptions) *corev1.Pod { objectMeta := metav1.ObjectMeta{ - Name: nextCommitHistory.Pod, + Name: devbox.Name, Namespace: devbox.Namespace, Labels: helper.GeneratePodLabels(devbox), Annotations: helper.GeneratePodAnnotations(devbox), } - ports := devbox.Spec.Config.Ports + // ports := devbox.Spec.Config.Ports // TODO: add extra ports to pod, currently not support // ports = append(ports, devbox.Spec.NetworkSpec.ExtraPorts...) envs := devbox.Spec.Config.Env - envs = append(envs, helper.GenerateDevboxEnvVars(devbox, nextCommitHistory)...) - - //get image name - var imageName string - if r.DebugMode { - imageName = devbox.Spec.Image - } else { - imageName = helper.GetLastSuccessCommitImageName(devbox) - } volumes := devbox.Spec.Config.Volumes volumes = append(volumes, helper.GenerateSSHVolume(devbox)) @@ -565,10 +522,9 @@ func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, next containers := []corev1.Container{ { - Name: devbox.ObjectMeta.Name, - Image: imageName, - Env: envs, - Ports: ports, + Name: devbox.ObjectMeta.Name, + Env: envs, + // Ports: ports, VolumeMounts: volumeMounts, WorkingDir: helper.GetWorkingDir(devbox), @@ -609,23 +565,12 @@ func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, next // set controller reference and finalizer _ = controllerutil.SetControllerReference(devbox, expectPod, r.Scheme) controllerutil.AddFinalizer(expectPod, devboxv1alpha1.FinalizerName) - return expectPod -} -func (r *DevboxReconciler) generateNextCommitHistory(devbox *devboxv1alpha1.Devbox) *devboxv1alpha1.CommitHistory { - now := time.Now() - return &devboxv1alpha1.CommitHistory{ - Image: r.generateImageName(devbox), - Time: metav1.Time{Time: now}, - Pod: devbox.Name + "-" + rand.String(5), - Status: devboxv1alpha1.CommitStatusPending, - PredicatedStatus: devboxv1alpha1.CommitStatusPending, + for _, opt := range opts { + opt(expectPod) } -} -func (r *DevboxReconciler) generateImageName(devbox *devboxv1alpha1.Devbox) string { - now := time.Now() - return fmt.Sprintf("%s/%s/%s:%s-%s", r.CommitImageRegistry, devbox.Namespace, devbox.Name, rand.String(5), now.Format("2006-01-02-150405")) + return expectPod } type ControllerRestartPredicate struct { @@ -650,7 +595,7 @@ func (p *ControllerRestartPredicate) Create(e event.CreateEvent) bool { func (r *DevboxReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). WithOptions(controller.Options{MaxConcurrentReconciles: 10}). - For(&devboxv1alpha1.Devbox{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). + For(&devboxv1alpha1.Devbox{}). Owns(&corev1.Pod{}, builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})). // enqueue request if pod spec/status is updated Owns(&corev1.Service{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). Owns(&corev1.Secret{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). diff --git a/controllers/devbox/internal/controller/devboxrelease_controller.go b/controllers/devbox/internal/controller/devboxrelease_controller.go deleted file mode 100644 index 5ca1b69117fb..000000000000 --- a/controllers/devbox/internal/controller/devboxrelease_controller.go +++ /dev/null @@ -1,171 +0,0 @@ -/* -Copyright 2024. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - "context" - "errors" - "fmt" - "time" - - reference "github.com/google/go-containerregistry/pkg/name" - - devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" - "github.com/labring/sealos/controllers/devbox/internal/controller/helper" - "github.com/labring/sealos/controllers/devbox/internal/controller/utils/registry" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/log" -) - -// DevBoxReleaseReconciler reconciles a DevBoxRelease object -type DevBoxReleaseReconciler struct { - client.Client - Registry *registry.Client - Scheme *runtime.Scheme -} - -// +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases/finalizers,verbs=update - -func (r *DevBoxReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - logger := log.FromContext(ctx) - devboxRelease := &devboxv1alpha1.DevBoxRelease{} - if err := r.Client.Get(ctx, req.NamespacedName, devboxRelease); err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - if devboxRelease.ObjectMeta.DeletionTimestamp.IsZero() { - if controllerutil.AddFinalizer(devboxRelease, devboxv1alpha1.FinalizerName) { - if err := r.Update(ctx, devboxRelease); err != nil { - return ctrl.Result{}, err - } - } - } else { - if controllerutil.RemoveFinalizer(devboxRelease, devboxv1alpha1.FinalizerName) { - if err := r.Update(ctx, devboxRelease); err != nil { - return ctrl.Result{}, err - } - } - return ctrl.Result{}, nil - } - - logger.Info("Reconciling DevBoxRelease", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag, "phase", devboxRelease.Status.Phase) - - if devboxRelease.Status.Phase == "" { - devboxRelease.Status.Phase = devboxv1alpha1.DevboxReleasePhasePending - err := r.Status().Update(ctx, devboxRelease) - if err != nil { - logger.Error(err, "Failed to update status", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag) - return ctrl.Result{}, err - } - return ctrl.Result{Requeue: true}, nil - } - - if devboxRelease.Status.Phase == devboxv1alpha1.DevboxReleasePhasePending { - logger.Info("Creating release tag", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag) - err := r.CreateReleaseTag(ctx, devboxRelease) - if err != nil && errors.Is(err, registry.ErrorManifestNotFound) { - logger.Info("Manifest not found, retrying", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag) - return ctrl.Result{RequeueAfter: time.Second * 10}, nil - } else if err != nil { - logger.Error(err, "Failed to create release tag", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag) - devboxRelease.Status.Phase = devboxv1alpha1.DevboxReleasePhaseFailed - _ = r.Status().Update(ctx, devboxRelease) - return ctrl.Result{}, err - } - logger.Info("Release tag created", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag) - devboxRelease.Status.Phase = devboxv1alpha1.DevboxReleasePhaseSuccess - if err = r.Status().Update(ctx, devboxRelease); err != nil { - logger.Error(err, "Failed to update status", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag) - return ctrl.Result{}, err - } - } - logger.Info("Reconciliation complete", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag) - return ctrl.Result{}, nil -} - -func (r *DevBoxReleaseReconciler) CreateReleaseTag(ctx context.Context, devboxRelease *devboxv1alpha1.DevBoxRelease) error { - logger := log.FromContext(ctx) - devbox := &devboxv1alpha1.Devbox{} - devboxInfo := types.NamespacedName{ - Name: devboxRelease.Spec.DevboxName, - Namespace: devboxRelease.Namespace, - } - if err := r.Get(ctx, devboxInfo, devbox); err != nil { - return err - } - // if original image is not set, set it to the last successful commit history - if devboxRelease.Status.OriginalImage == "" { - commitHistory := helper.GetLastPredicatedSuccessCommitHistory(devbox) - if commitHistory == nil { - return fmt.Errorf("no successful commit history found") - } - devboxRelease.Status.OriginalImage = commitHistory.Image - } - // get image info from devbox last successful commit history image and devbox release original image - hostName, imageName, oldTag, err := r.GetImageInfo(devbox, devboxRelease) - if err != nil { - return err - } - logger.Info("Tagging image", "host", hostName, "image", imageName, "oldTag", oldTag, "newTag", devboxRelease.Spec.NewTag) - if err = r.Status().Update(ctx, devboxRelease); err != nil { - logger.Error(err, "Failed to update status", "devbox", devboxRelease.Spec.DevboxName, "newTag", devboxRelease.Spec.NewTag) - return err - } - return r.Registry.TagImage(hostName, imageName, oldTag, devboxRelease.Spec.NewTag) -} - -func (r *DevBoxReleaseReconciler) DeleteReleaseTag(_ context.Context, _ *devboxv1alpha1.DevBoxRelease) error { - //todo only delete CR without doing any other operations - return nil -} - -func (r *DevBoxReleaseReconciler) GetImageInfo(devbox *devboxv1alpha1.Devbox, devboxRelease *devboxv1alpha1.DevBoxRelease) (string, string, string, error) { - res, err := reference.ParseReference(devboxRelease.Status.OriginalImage) - if err != nil { - return "", "", "", err - } - registryStr := res.Context().RegistryStr() - // if registry is empty, use the last successful commit history image's registry - if registryStr == "" { - commitHistory := helper.GetLastPredicatedSuccessCommitHistory(devbox) - if commitHistory == nil { - return "", "", "", fmt.Errorf("no successful commit history found") - } - historyRepo, err := reference.ParseReference(commitHistory.Image) - if err != nil { - return "", "", "", err - } - registryStr = historyRepo.Context().RegistryStr() - } - return registryStr, res.Context().RepositoryStr(), res.Identifier(), nil -} - -// SetupWithManager sets up the controller with the Manager. -func (r *DevBoxReleaseReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - WithOptions(controller.Options{MaxConcurrentReconciles: 10}). - For(&devboxv1alpha1.DevBoxRelease{}). - Complete(r) -} diff --git a/controllers/devbox/internal/controller/devboxrelease_controller_test.go b/controllers/devbox/internal/controller/devboxrelease_controller_test.go deleted file mode 100644 index e42ba0be05c8..000000000000 --- a/controllers/devbox/internal/controller/devboxrelease_controller_test.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright 2024. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - "context" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" -) - -var _ = Describe("DevBoxRelease Controller", func() { - Context("When reconciling a resource", func() { - const resourceName = "test-resource" - - ctx := context.Background() - - typeNamespacedName := types.NamespacedName{ - Name: resourceName, - Namespace: "default", // TODO(user):Modify as needed - } - devboxrelease := &devboxv1alpha1.DevBoxRelease{} - - BeforeEach(func() { - By("creating the custom resource for the Kind DevBoxRelease") - err := k8sClient.Get(ctx, typeNamespacedName, devboxrelease) - if err != nil && errors.IsNotFound(err) { - resource := &devboxv1alpha1.DevBoxRelease{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourceName, - Namespace: "default", - }, - // TODO(user): Specify other spec details if needed. - } - Expect(k8sClient.Create(ctx, resource)).To(Succeed()) - } - }) - - AfterEach(func() { - // TODO(user): Cleanup logic after each test, like removing the resource instance. - resource := &devboxv1alpha1.DevBoxRelease{} - err := k8sClient.Get(ctx, typeNamespacedName, resource) - Expect(err).NotTo(HaveOccurred()) - - By("Cleanup the specific resource instance DevBoxRelease") - Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) - }) - It("should successfully reconcile the resource", func() { - By("Reconciling the created resource") - controllerReconciler := &DevBoxReleaseReconciler{ - Client: k8sClient, - Scheme: k8sClient.Scheme(), - } - - _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ - NamespacedName: typeNamespacedName, - }) - Expect(err).NotTo(HaveOccurred()) - // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. - // Example: If you expect a certain status condition after reconciliation, verify it here. - }) - }) -}) diff --git a/controllers/devbox/internal/controller/helper/devbox.go b/controllers/devbox/internal/controller/helper/devbox.go index d73b0a9e7070..fd214c2a00f1 100644 --- a/controllers/devbox/internal/controller/helper/devbox.go +++ b/controllers/devbox/internal/controller/helper/devbox.go @@ -15,8 +15,6 @@ package helper import ( - "fmt" - "sort" "strings" "crypto/ed25519" @@ -29,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" "k8s.io/utils/ptr" + "github.com/google/uuid" devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" utilsresource "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" "github.com/labring/sealos/controllers/devbox/label" @@ -38,6 +37,55 @@ const ( DevBoxPartOf = "devbox" ) +type DevboxPodOptions func(pod *corev1.Pod) + +func WithPodImage(image string) DevboxPodOptions { + return func(pod *corev1.Pod) { + pod.Spec.Containers[0].Image = image + } +} + +func WithPodContentID(contentID string) DevboxPodOptions { + return func(pod *corev1.Pod) { + if pod.Annotations == nil { + pod.Annotations = make(map[string]string) + } + pod.Annotations[devboxv1alpha1.AnnotationContentID] = contentID + } +} + +func WithPodAnnotations(annotations map[string]string) DevboxPodOptions { + return func(pod *corev1.Pod) { + if pod.Annotations == nil { + pod.Annotations = make(map[string]string) + } + for k, v := range annotations { + pod.Annotations[k] = v + } + } +} + +func WithPodLabels(labels map[string]string) DevboxPodOptions { + return func(pod *corev1.Pod) { + if pod.Labels == nil { + pod.Labels = make(map[string]string) + } + for k, v := range labels { + pod.Labels[k] = v + } + } +} + +func WithPodNodeName(nodeName string) DevboxPodOptions { + return func(pod *corev1.Pod) { + pod.Spec.NodeName = nodeName + } +} + +func NewContentID() string { + return uuid.New().String() +} + func GeneratePodLabels(devbox *devboxv1alpha1.Devbox) map[string]string { labels := make(map[string]string) @@ -64,6 +112,7 @@ func GeneratePodAnnotations(devbox *devboxv1alpha1.Devbox) map[string]string { annotations[k] = v } } + annotations[devboxv1alpha1.AnnotationStorageLimit] = devbox.Spec.StorageLimit return annotations } @@ -101,26 +150,6 @@ func GenerateDevboxPhase(devbox *devboxv1alpha1.Devbox, podList corev1.PodList) return devboxv1alpha1.DevboxPhaseUnknown } -func MergeCommitHistory(devbox *devboxv1alpha1.Devbox, latestDevbox *devboxv1alpha1.Devbox) []*devboxv1alpha1.CommitHistory { - res := make([]*devboxv1alpha1.CommitHistory, 0) - historyMap := make(map[string]*devboxv1alpha1.CommitHistory) - for _, c := range latestDevbox.Status.CommitHistory { - historyMap[c.Pod] = c - } - // up coming commit history will be added to the latest devbox - for _, c := range devbox.Status.CommitHistory { - historyMap[c.Pod] = c - } - for _, c := range historyMap { - res = append(res, c) - } - // sort commit history by time in descending order - sort.Slice(res, func(i, j int) bool { - return res[i].Time.After(res[j].Time.Time) - }) - return res -} - func GenerateSSHKeyPair() ([]byte, []byte, error) { pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { @@ -139,153 +168,6 @@ func GenerateSSHKeyPair() ([]byte, []byte, error) { return sshPublicKey, privateKey, nil } -func UpdatePredicatedCommitStatus(devbox *devboxv1alpha1.Devbox, pod *corev1.Pod) { - for i, c := range devbox.Status.CommitHistory { - if c.Pod == pod.Name { - devbox.Status.CommitHistory[i].PredicatedStatus = PredicateCommitStatus(pod) - break - } - } -} - -// UpdateDevboxStatus updates the devbox status, including phase, pod phase, last terminated state and commit history, maybe we need update more fields in the future -// TODO: move this function to devbox types.go -func UpdateDevboxStatus(current, latest *devboxv1alpha1.Devbox) { - latest.Status.Phase = current.Status.Phase - latest.Status.State = current.Status.State - latest.Status.LastTerminationState = current.Status.LastTerminationState - latest.Status.CommitHistory = MergeCommitHistory(current, latest) -} - -func UpdateCommitHistory(devbox *devboxv1alpha1.Devbox, pod *corev1.Pod, updateStatus bool) { - // update commit history, if devbox commit history missed the pod, we need add it - found := false - for i, c := range devbox.Status.CommitHistory { - if c.Pod == pod.Name { - found = true - if updateStatus { - devbox.Status.CommitHistory[i].Status = devbox.Status.CommitHistory[i].PredicatedStatus - } - if len(pod.Status.ContainerStatuses) > 0 { - devbox.Status.CommitHistory[i].Node = pod.Spec.NodeName - devbox.Status.CommitHistory[i].ContainerID = pod.Status.ContainerStatuses[0].ContainerID - } - break - } - } - if !found { - newCommitHistory := &devboxv1alpha1.CommitHistory{ - Pod: pod.Name, - PredicatedStatus: PredicateCommitStatus(pod), - } - if len(pod.Status.ContainerStatuses) > 0 { - newCommitHistory.ContainerID = pod.Status.ContainerStatuses[0].ContainerID - newCommitHistory.Node = pod.Spec.NodeName - } - if updateStatus { - newCommitHistory.Status = newCommitHistory.PredicatedStatus - } - devbox.Status.CommitHistory = append(devbox.Status.CommitHistory, newCommitHistory) - } -} - -func podContainerID(pod *corev1.Pod) string { - if len(pod.Status.ContainerStatuses) > 0 { - return pod.Status.ContainerStatuses[0].ContainerID - } - return "" -} -func PredicateCommitStatus(pod *corev1.Pod) devboxv1alpha1.CommitStatus { - if podContainerID(pod) == "" { - return devboxv1alpha1.CommitStatusPending - } - return devboxv1alpha1.CommitStatusSuccess -} - -func GenerateDevboxEnvVars(devbox *devboxv1alpha1.Devbox, nextCommitHistory *devboxv1alpha1.CommitHistory) []corev1.EnvVar { - // if devbox.Spec.Squash is true, and devbox.Status.CommitHistory has success commit history, we need to set SEALOS_COMMIT_IMAGE_SQUASH to true - doSquash := false - if devbox.Spec.Squash && len(devbox.Status.CommitHistory) > 0 { - for _, commit := range devbox.Status.CommitHistory { - if commit.Status == devboxv1alpha1.CommitStatusSuccess { - doSquash = true - break - } - } - } - - return []corev1.EnvVar{ - { - Name: "SEALOS_COMMIT_ON_STOP", - Value: "true", - }, - { - Name: "SEALOS_COMMIT_IMAGE_NAME", - Value: nextCommitHistory.Image, - }, - { - Name: "SEALOS_COMMIT_IMAGE_SQUASH", - Value: fmt.Sprintf("%v", doSquash), - }, - { - Name: "SEALOS_DEVBOX_NAME", - Value: devbox.Namespace + "-" + devbox.Name, - }, - { - Name: "SEALOS_DEVBOX_POD_UID", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.uid", - }, - }, - }, - } -} - -func GetLastSuccessCommitHistory(devbox *devboxv1alpha1.Devbox) *devboxv1alpha1.CommitHistory { - if len(devbox.Status.CommitHistory) == 0 { - return nil - } - // Sort commit history by time in descending order - sort.Slice(devbox.Status.CommitHistory, func(i, j int) bool { - return devbox.Status.CommitHistory[i].Time.After(devbox.Status.CommitHistory[j].Time.Time) - }) - - for _, commit := range devbox.Status.CommitHistory { - if commit.Status == devboxv1alpha1.CommitStatusSuccess { - return commit - } - } - return nil -} - -func GetLastPredicatedSuccessCommitHistory(devbox *devboxv1alpha1.Devbox) *devboxv1alpha1.CommitHistory { - if len(devbox.Status.CommitHistory) == 0 { - return nil - } - // Sort commit history by time in descending order - sort.Slice(devbox.Status.CommitHistory, func(i, j int) bool { - return devbox.Status.CommitHistory[i].Time.After(devbox.Status.CommitHistory[j].Time.Time) - }) - for _, commit := range devbox.Status.CommitHistory { - if commit.PredicatedStatus == devboxv1alpha1.CommitStatusSuccess { - return commit - } - } - return nil -} - -func GetLastSuccessCommitImageName(devbox *devboxv1alpha1.Devbox) string { - if len(devbox.Status.CommitHistory) == 0 { - return devbox.Spec.Image - } - commit := GetLastSuccessCommitHistory(devbox) - if commit == nil { - return devbox.Spec.Image - } - return commit.Image -} - func GenerateSSHVolumeMounts() []corev1.VolumeMount { return []corev1.VolumeMount{ { diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go new file mode 100644 index 000000000000..483b4da423a1 --- /dev/null +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -0,0 +1,158 @@ +package controller + +import ( + "context" + "fmt" + "time" + + "github.com/go-logr/logr" + "github.com/google/uuid" + devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + "github.com/labring/sealos/controllers/devbox/internal/commit" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type StateChangeHandler struct { + Committer commit.Committer + CommitImageRegistry string + NodeName string + + Logger logr.Logger + Client client.Client + Scheme *runtime.Scheme + Recorder record.EventRecorder +} + +// todo: handle state change event +func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) error { + h.Logger.Info("StateChangeHandler.Handle called", + "event", event.Name, + "eventSourceHost", event.Source.Host, + "handlerNodeName", h.NodeName, + "eventType", event.Type, + "eventReason", event.Reason, + "eventMessage", event.Message) + + if event.Source.Host != h.NodeName { + h.Logger.Info("event source host is not the node name, skip", "event", event) + return nil + } + devbox := &devboxv1alpha1.Devbox{} + if err := h.Client.Get(ctx, types.NamespacedName{Namespace: event.Namespace, Name: event.InvolvedObject.Name}, devbox); err != nil { + h.Logger.Error(err, "failed to get devbox", "devbox", event.InvolvedObject.Name) + return err + } + switch devbox.Status.State { + case devboxv1alpha1.DevboxStateRunning: + switch devbox.Spec.State { + case devboxv1alpha1.DevboxStateStopped: + // do not commit, update devbox status state to stopped + devbox.Status.State = devboxv1alpha1.DevboxStateStopped + h.Logger.Info("update devbox status from running to stopped", "devbox", devbox.Name) + if err := h.Client.Status().Update(ctx, devbox); err != nil { + h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) + return err + } + case devboxv1alpha1.DevboxStateShutdown: + // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id + // step 1: do commit + baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage + commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage + h.Logger.Info("commit devbox", "devbox", devbox.Name, "baseImage", baseImage, "commitImage", commitImage) + if err := h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { + h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) + return err + } + // step 2: update devbox commit record + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusSuccess + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitTime = metav1.Now() + // step 3: update devbox status state to shutdown + devbox.Status.State = devboxv1alpha1.DevboxStateShutdown + // step 4: add a new commit record for the new content id + // make sure that always have a new commit record for shutdown state + devbox.Status.ContentID = uuid.New().String() + devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha1.CommitRecord{ + CommitStatus: devboxv1alpha1.CommitStatusPending, + Node: "", + BaseImage: commitImage, + CommitImage: h.generateImageName(devbox), + GenerateTime: metav1.Now(), + } + h.Logger.Info("update devbox status from running to shutdown", "devbox", devbox.Name) + if err := h.Client.Status().Update(ctx, devbox); err != nil { + h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) + return err + } + } + case devboxv1alpha1.DevboxStateStopped: + switch devbox.Spec.State { + case devboxv1alpha1.DevboxStateRunning: + // do not commit, update devbox status state to running + devbox.Status.State = devboxv1alpha1.DevboxStateRunning + h.Logger.Info("update devbox status from stopped to running", "devbox", devbox.Name) + if err := h.Client.Status().Update(ctx, devbox); err != nil { + h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) + return err + } + case devboxv1alpha1.DevboxStateShutdown: + // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id + // step 1: do commit + baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage + commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage + h.Logger.Info("commit devbox", "devbox", devbox.Name, "baseImage", baseImage, "commitImage", commitImage) + if err := h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { + h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) + return err + } + // step 2: update devbox commit record + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusSuccess + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitTime = metav1.Now() + // step 3: update devbox status state to shutdown + devbox.Status.State = devboxv1alpha1.DevboxStateShutdown + // step 4: add a new commit record for the new content id + // make sure that always have a new commit record for shutdown state + devbox.Status.ContentID = uuid.New().String() + devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha1.CommitRecord{ + CommitStatus: devboxv1alpha1.CommitStatusPending, + Node: "", + BaseImage: commitImage, + CommitImage: h.generateImageName(devbox), + GenerateTime: metav1.Now(), + } + h.Logger.Info("update devbox status from stopped to shutdown", "devbox", devbox.Name) + if err := h.Client.Status().Update(ctx, devbox); err != nil { + h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) + return err + } + } + case devboxv1alpha1.DevboxStateShutdown: + switch devbox.Spec.State { + case devboxv1alpha1.DevboxStateRunning: + // do not commit, update devbox status state to running + devbox.Status.State = devboxv1alpha1.DevboxStateRunning + h.Logger.Info("update devbox status from shutdown to running", "devbox", devbox.Name) + if err := h.Client.Status().Update(ctx, devbox); err != nil { + h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) + return err + } + case devboxv1alpha1.DevboxStateStopped: + // do nothing, shutdown state is not allowed to be changed to stopped state + h.Recorder.Eventf(devbox, corev1.EventTypeWarning, "Shutdown state is not allowed to be changed to stopped state", "Shutdown state is not allowed to be changed to stopped state") + h.Logger.Error(fmt.Errorf("shutdown state is not allowed to be changed to stopped state"), "shutdown state is not allowed to be changed to stopped state", "devbox", devbox.Name) + return fmt.Errorf("shutdown state is not allowed to be changed to stopped state") + } + } + return nil +} + +func (h *StateChangeHandler) generateImageName(devbox *devboxv1alpha1.Devbox) string { + now := time.Now() + return fmt.Sprintf("%s/%s/%s:%s-%s", h.CommitImageRegistry, devbox.Namespace, devbox.Name, rand.String(5), now.Format("2006-01-02-150405")) +} diff --git a/controllers/devbox/internal/controller/utils/matcher/matcher.go b/controllers/devbox/internal/controller/utils/matcher/matcher.go index dfad8e59f9f6..54d5d94a6e6d 100644 --- a/controllers/devbox/internal/controller/utils/matcher/matcher.go +++ b/controllers/devbox/internal/controller/utils/matcher/matcher.go @@ -18,6 +18,8 @@ import ( "log/slog" corev1 "k8s.io/api/core/v1" + + devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" ) type PodMatcher interface { @@ -140,6 +142,12 @@ func (p PortMatcher) Match(expectPod *corev1.Pod, pod *corev1.Pod) bool { return true } +type StorageLimitMatcher struct{} + +func (s StorageLimitMatcher) Match(expectPod *corev1.Pod, pod *corev1.Pod) bool { + return expectPod.ObjectMeta.Annotations[devboxv1alpha1.AnnotationStorageLimit] == pod.ObjectMeta.Annotations[devboxv1alpha1.AnnotationStorageLimit] +} + // PredicateCommitStatus returns the commit status of the pod // if the pod container id is empty, it means the pod is pending or has't started, we can assume the image has not been committed // otherwise, it means the pod has been started, we can assume the image has been committed diff --git a/controllers/devbox/internal/controller/utils/nodes/node.go b/controllers/devbox/internal/controller/utils/nodes/node.go new file mode 100644 index 000000000000..b9a5be73716f --- /dev/null +++ b/controllers/devbox/internal/controller/utils/nodes/node.go @@ -0,0 +1,14 @@ +package nodes + +import ( + "os" +) + +func GetNodeName() string { + nodeName := os.Getenv("NODE_NAME") + if nodeName == "" { + // panic if node name is not set + panic("NODE_NAME is not set") + } + return nodeName +} diff --git a/controllers/go.work.sum b/controllers/go.work.sum index b8bc5312c233..03baad967dec 100644 --- a/controllers/go.work.sum +++ b/controllers/go.work.sum @@ -1,6 +1,10 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -1439,6 +1443,7 @@ cloud.google.com/go/speech v1.20.1/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWo cloud.google.com/go/speech v1.21.0 h1:qkxNao58oF8ghAHE1Eghen7XepawYEN5zuZXYWaUTA4= cloud.google.com/go/speech v1.21.0/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= cloud.google.com/go/speech v1.21.1/go.mod h1:E5GHZXYQlkqWQwY5xRSLHw2ci5NMQNG52FfMU1aZrIA= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= @@ -1656,9 +1661,7 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zum git.sr.ht/~sbinet/gg v0.3.1 h1:LNhjNn8DerC8f9DHLz6lS0YYul/b602DUxDgGkd/Aik= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -1951,6 +1954,7 @@ github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= @@ -1997,7 +2001,6 @@ github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4S github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0= github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= @@ -2048,7 +2051,6 @@ github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZ github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= @@ -2067,6 +2069,7 @@ github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJ github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo= github.com/containerd/imgcrypt v1.1.7/go.mod h1:FD8gqIcX5aTotCtOmjeCsi3A1dHmTZpnMISGKSczt4k= +github.com/containerd/imgcrypt v1.1.8/go.mod h1:x6QvFIkMyO2qGIY2zXc88ivEzcbgvLdWjoZyGqDap5U= 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.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= @@ -2074,6 +2077,7 @@ github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oM github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.3.0/go.mod h1:Zw9q2lP16sdg0zYybemZ9yTDy8g7fPCIB3KXOGlggXI= github.com/containerd/nri v0.4.0/go.mod h1:Zw9q2lP16sdg0zYybemZ9yTDy8g7fPCIB3KXOGlggXI= +github.com/containerd/nri v0.8.0/go.mod h1:uSkgBrCdEtAiEz4vnrq8gmAC4EnVAM5Klt0OuK5rZYQ= github.com/containerd/protobuild v0.3.0/go.mod h1:5mNMFKKAwCIAkFBPiOdtRx2KiQlyEJeMXnL5R1DsWu8= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= @@ -2089,7 +2093,6 @@ github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kw github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= @@ -2119,6 +2122,7 @@ github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pABH85425Es2g= github.com/containers/ocicrypt v1.1.6/go.mod h1:WgjxPWdTJMqYMjf3M6cuIFFA1/MpyyhIM99YInA+Rvc= github.com/containers/ocicrypt v1.1.7/go.mod h1:7CAhjcj2H8AYp5YvEie7oVSK2AhBY8NscCYRawuDNtw= +github.com/containers/ocicrypt v1.1.10/go.mod h1:YfzSSr06PTHQwSTUKqDSjish9BeW1E4HUmreluQcMd8= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -2230,7 +2234,6 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-connections v0.4.1-0.20190612165340-fd1b1942c4d5 h1:2o8D0hdBky229bNnc7a8bAZkeVMpH4qsp2Rmt4g/+Zk= github.com/docker/go-connections v0.4.1-0.20190612165340-fd1b1942c4d5/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= @@ -2260,6 +2263,7 @@ github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw= github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= @@ -2347,6 +2351,7 @@ github.com/go-gorp/gorp/v3 v3.0.5 h1:PUjzYdYu3HBOh8LE+UUmRG2P0IRDak9XMeGNvaeq4Ow github.com/go-gorp/gorp/v3 v3.0.5/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.66.6/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= @@ -2484,9 +2489,13 @@ github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoG github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -2546,6 +2555,7 @@ github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97Dwqy github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= @@ -2689,6 +2699,7 @@ github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= github.com/intel/goresctrl v0.3.0/go.mod h1:fdz3mD85cmP9sHD8JUlrNWAxvwM86CrbmVXltEKd7zk= +github.com/intel/goresctrl v0.5.0/go.mod h1:mIe63ggylWYr0cU/l8n11FAkesqfvuP3oktIsxvu0T0= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= @@ -2757,6 +2768,7 @@ github.com/kopia/kopia v0.10.7 h1:6s0ZIZW3Ge2ozzefddASy7CIUadp/5tF9yCDKQfAKKI= github.com/kopia/kopia v0.10.7/go.mod h1:0d9THPD+jwomPcXvPbCdmLyX6phQVP7AqcCcDEajfNA= github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= @@ -2902,9 +2914,7 @@ github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVO github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= @@ -3005,6 +3015,8 @@ github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1 github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= @@ -3059,6 +3071,7 @@ github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5d github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/performancecopilot/speed/v4 v4.0.0 h1:VxEDCmdkfbQYDlcr/GC9YoN9PQ6p8ulk9xVsepYy9ZY= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -3082,6 +3095,7 @@ github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdL github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= @@ -3256,6 +3270,7 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec h1:q6XVwXmKvCRHRqesF3cSv6lNqqHi0QWOvgDlSohg8UA= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e h1:mOtuXaRAbVZsxAHVdPR3IjfmN8T1h2iczJLynhLybf8= @@ -3391,6 +3406,7 @@ go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= @@ -3465,7 +3481,7 @@ go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= @@ -3656,12 +3672,14 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= @@ -3697,8 +3715,11 @@ golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeap golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20220302094943-723b81ca9867 h1:TcHcE0vrmgzNH1v3ppjcMGbhG5+9fMuvOmUYwNEF4q4= golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= @@ -3714,6 +3735,7 @@ golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -3724,7 +3746,10 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -3763,6 +3788,10 @@ golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -3821,19 +3850,25 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -3907,6 +3942,7 @@ golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= @@ -3934,8 +3970,15 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200313205530-4303120df7d8/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -3951,6 +3994,7 @@ golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= @@ -3980,6 +4024,9 @@ gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= gonum.org/v1/plot v0.10.1 h1:dnifSs43YJuNMDzB7v8wV64O4ABBHReuAVAoBxqBqS4= gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= @@ -4045,9 +4092,16 @@ google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dT google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -4262,6 +4316,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= @@ -4275,6 +4330,9 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= @@ -4303,6 +4361,7 @@ google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpX google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= @@ -4565,3 +4624,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ih sigs.k8s.io/structured-merge-diff/v4 v4.4.3/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +tags.cncf.io/container-device-interface v0.8.1/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y= +tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws= From 05b02184a2af70f326b869ff16df4141c0b23bcf Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Mon, 28 Jul 2025 15:52:37 +0800 Subject: [PATCH 02/46] cotainer commit (#38) * Commit * fix conflict * commit container * delete container * stdout * Gc Handler * ctx * unit test * pull i * unit test * go mod * fix err * gc unit test * delete useless * rename * rename * import order --------- Co-authored-by: Cunzili --- controllers/devbox/go.mod | 183 ++++-- controllers/devbox/go.sum | 541 +++++++++++++++--- controllers/devbox/internal/commit/commit.go | 231 +++++++- .../devbox/internal/commit/commit_test.go | 240 ++++++++ controllers/devbox/internal/commit/const.go | 14 + controllers/go.work.sum | 231 ++++++-- service/go.work.sum | 9 + 7 files changed, 1285 insertions(+), 164 deletions(-) create mode 100644 controllers/devbox/internal/commit/commit_test.go create mode 100644 controllers/devbox/internal/commit/const.go diff --git a/controllers/devbox/go.mod b/controllers/devbox/go.mod index 58d1e7521e87..3b30825840f8 100644 --- a/controllers/devbox/go.mod +++ b/controllers/devbox/go.mod @@ -3,97 +3,204 @@ module github.com/labring/sealos/controllers/devbox go 1.24.0 require ( + github.com/containerd/containerd/v2 v2.1.3 + github.com/containerd/nerdctl/v2 v2.1.3 + github.com/go-logr/logr v1.4.2 github.com/google/uuid v1.6.0 - github.com/onsi/ginkgo/v2 v2.22.0 - github.com/onsi/gomega v1.36.1 - golang.org/x/crypto v0.31.0 - k8s.io/api v0.32.1 - k8s.io/apimachinery v0.32.1 - k8s.io/client-go v0.32.1 + github.com/onsi/ginkgo/v2 v2.23.4 + github.com/onsi/gomega v1.37.0 + github.com/stretchr/testify v1.10.0 + golang.org/x/crypto v0.40.0 + google.golang.org/grpc v1.72.2 + k8s.io/api v0.32.3 + k8s.io/apimachinery v0.32.3 + k8s.io/client-go v0.32.3 + k8s.io/cri-api v0.32.3 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 sigs.k8s.io/controller-runtime v0.20.4 ) +replace github.com/containerd/nerdctl => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 + +replace github.com/containerd/nerdctl/v2 => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 + +replace github.com/containerd/nerdctl/mod/tigron => github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2 + require ( - cel.dev/expr v0.18.0 // indirect + cel.dev/expr v0.20.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/Microsoft/hcsshim v0.13.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cilium/ebpf v0.16.0 // indirect + github.com/containerd/accelerated-container-image v1.3.0 // indirect + github.com/containerd/cgroups/v3 v3.0.5 // indirect + github.com/containerd/console v1.0.5 // indirect + github.com/containerd/containerd/api v1.9.0 // indirect + github.com/containerd/continuity v0.4.5 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/fifo v1.1.0 // indirect + github.com/containerd/go-cni v1.1.12 // indirect + github.com/containerd/go-runc v1.1.0 // indirect + github.com/containerd/imgcrypt/v2 v2.0.1 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/nydus-snapshotter v0.15.2 // indirect + github.com/containerd/platforms v1.0.0-rc.1 // indirect + github.com/containerd/plugin v1.0.0 // indirect + github.com/containerd/stargz-snapshotter v0.16.3 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect + github.com/containerd/stargz-snapshotter/ipfs v0.16.3 // indirect + github.com/containerd/ttrpc v1.2.7 // indirect + github.com/containerd/typeurl/v2 v2.2.3 // indirect + github.com/containernetworking/cni v1.3.0 // indirect + github.com/containernetworking/plugins v1.7.1 // indirect + github.com/containers/ocicrypt v1.2.1 // indirect + github.com/coreos/go-iptables v0.8.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/djherbis/times v1.6.0 // indirect + github.com/docker/cli v28.3.2+incompatible // indirect + github.com/docker/docker v28.3.2+incompatible // indirect + github.com/docker/docker-credential-helpers v0.8.2 // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/fahedouch/go-logrotate v0.3.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fluent/fluent-logger-golang v1.10.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/go-viper/mapstructure/v2 v2.3.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.22.0 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/ipfs/go-cid v0.5.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/miekg/pkcs11 v1.1.1 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/sys/mount v0.3.4 // indirect + github.com/moby/sys/mountinfo v0.7.2 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/signal v0.7.1 // indirect + github.com/moby/sys/symlink v0.3.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.13.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-varint v0.0.7 // 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.1 // indirect + github.com/opencontainers/runtime-spec v1.2.1 // indirect + github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect + github.com/opencontainers/selinux v1.12.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect + github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/spf13/cobra v1.8.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/rootless-containers/bypass4netns v0.4.2 // indirect + github.com/rootless-containers/rootlesskit/v2 v2.3.5 // indirect + github.com/sasha-s/go-deadlock v0.3.5 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/smallstep/pkcs7 v0.1.1 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect + github.com/tinylib/msgp v1.3.0 // indirect + github.com/vbatts/tar-split v0.11.6 // indirect + github.com/vishvananda/netlink v1.3.1 // indirect + github.com/vishvananda/netns v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect - go.opentelemetry.io/proto/otlp v1.3.1 // indirect + github.com/yuchanns/srslog v1.1.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/sdk v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/proto/otlp v1.5.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/term v0.33.0 // indirect + golang.org/x/text v0.27.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.26.0 // indirect + golang.org/x/tools v0.34.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.35.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.32.1 // indirect - k8s.io/apiserver v0.32.1 // indirect - k8s.io/component-base v0.32.1 // indirect + k8s.io/apiserver v0.32.3 // indirect + k8s.io/component-base v0.32.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + lukechampine.com/blake3 v1.3.0 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect + tags.cncf.io/container-device-interface v1.0.1 // indirect + tags.cncf.io/container-device-interface/specs-go v1.0.0 // indirect ) diff --git a/controllers/devbox/go.sum b/controllers/devbox/go.sum index 7018ef5deb76..acef4f142598 100644 --- a/controllers/devbox/go.sum +++ b/controllers/devbox/go.sum @@ -1,5 +1,19 @@ -cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= -cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI= +cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA= +github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= @@ -10,26 +24,113 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= +github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/compose-spec/compose-go/v2 v2.7.1 h1:EUIbuaD0R/J1KA+FbJMNbcS9+jt/CVudbp5iHqUllSs= +github.com/compose-spec/compose-go/v2 v2.7.1/go.mod h1:TmjkIB9W73fwVxkYY+u2uhMbMUakjiif79DlYgXsyvU= +github.com/containerd/accelerated-container-image v1.3.0 h1:sFbTgSuMboeKHa9f7MY11hWF1XxVWjFoiTsXYtOtvdU= +github.com/containerd/accelerated-container-image v1.3.0/go.mod h1:EvKVWor6ZQNUyYp0MZm5hw4k21ropuz7EegM+m/Jb/Q= +github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo= +github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= +github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= +github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0= +github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI= +github.com/containerd/containerd/v2 v2.1.3 h1:eMD2SLcIQPdMlnlNF6fatlrlRLAeDaiGPGwmRKLZKNs= +github.com/containerd/containerd/v2 v2.1.3/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= +github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= +github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= +github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= +github.com/containerd/go-cni v1.1.12 h1:wm/5VD/i255hjM4uIZjBRiEQ7y98W9ACy/mHeLi4+94= +github.com/containerd/go-cni v1.1.12/go.mod h1:+jaqRBdtW5faJxj2Qwg1Of7GsV66xcvnCx4mSJtUlxU= +github.com/containerd/go-runc v1.1.0 h1:OX4f+/i2y5sUT7LhmcJH7GYrjjhHa1QI4e8yO0gGleA= +github.com/containerd/go-runc v1.1.0/go.mod h1:xJv2hFF7GvHtTJd9JqTS2UVxMkULUYw4JN5XAUZqH5U= +github.com/containerd/imgcrypt/v2 v2.0.1 h1:gQcmeCKA97fAl0wlpq0itSY/PagFBsn4/mlKUy6kOio= +github.com/containerd/imgcrypt/v2 v2.0.1/go.mod h1:/qIJL8nxzdzMA2n5iYyyuIY36KfoVQWmgTWdfVtyebM= +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/nydus-snapshotter v0.15.2 h1:qsHI4M+Wwrf6Jr4eBqhNx8qh+YU0dSiJ+WPmcLFWNcg= +github.com/containerd/nydus-snapshotter v0.15.2/go.mod h1:FfwH2KBkNYoisK/e+KsmNr7xTU53DmnavQHMFOcXwfM= +github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E= +github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y= +github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8= +github.com/containerd/stargz-snapshotter v0.16.3 h1:zbQMm8dRuPHEOD4OqAYGajJJUwCeUzt4j7w9Iaw58u4= +github.com/containerd/stargz-snapshotter v0.16.3/go.mod h1:XPOl2oa9zjWidTM2IX191smolwWc3/zkKtp02TzTFb0= +github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= +github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= +github.com/containerd/stargz-snapshotter/ipfs v0.16.3 h1:d6IBSzYo0vlFcujwTqJRwpI3cZgX3E2I6Ev7LtMaZ4M= +github.com/containerd/stargz-snapshotter/ipfs v0.16.3/go.mod h1:d4EuGnC3RteInKAdddUbDOL88uw3vZySSLZ44pbriGM= +github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= +github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= +github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= +github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo= +github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= +github.com/containernetworking/plugins v1.7.1 h1:CNAR0jviDj6FS5Vg85NTgKWLDzZPfi/lj+VJfhMDTIs= +github.com/containernetworking/plugins v1.7.1/go.mod h1:xuMdjuio+a1oVQsHKjr/mgzuZ24leAsqUYRnzGoXHy0= +github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM= +github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ= +github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc= +github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +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.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= +github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= +github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY= +github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA= +github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= +github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +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/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/fahedouch/go-logrotate v0.3.0 h1:XP+dHIDgWZ1ckz43mG6gl5ASer3PZDVr755SVMyzaUQ= +github.com/fahedouch/go-logrotate v0.3.0/go.mod h1:X49m0bvPLkk71MHNCQ1yEfVEw8W/u+qvHa/hOnhCYf4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fluent/fluent-logger-golang v1.10.0 h1:JcLj8u3WclQv2juHGKTSzBRM5vIZjEqbrmvn/n+m1W0= +github.com/fluent/fluent-logger-golang v1.10.0/go.mod h1:UNyv8FAGmQcYJRtk+yfxhWqWUwsabTipgjXvBDR8kTs= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -45,10 +146,31 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= +github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= @@ -57,26 +179,50 @@ github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g= github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= +github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= +github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -84,131 +230,363 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2 h1:ZZSdAQJUwb+HyWle8w+B+vLZodSJaeKMojEOdWXYuUU= +github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2/go.mod h1:gmUZh2wUVxr/msGogKUi6v9eJbP5ASO4fVYEPzHH4iI= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 h1:Xd/Ucu301cld+j+E/+nf0aeQ5HOwmZfJ+WH2dCjCy3Y= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2/go.mod h1:un/Eqg9DrVwazXHukMsxq+PEBixrN7YC3NRXdx5J3tY= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= +github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= +github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= +github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mount v0.3.4 h1:yn5jq4STPztkkzSKpZkLcmjue+bZJ0u2AuQY1iNI1Ww= +github.com/moby/sys/mount v0.3.4/go.mod h1:KcQJMbQdJHPlq5lcYT+/CjatWM4PuxKe+XLSVS4J6Os= +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= +github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= +github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU= +github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ= +github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= -github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= -github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= +github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww= +github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0= +github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI= +github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8= +github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rootless-containers/bypass4netns v0.4.2 h1:JUZcpX7VLRfDkLxBPC6fyNalJGv9MjnjECOilZIvKRc= +github.com/rootless-containers/bypass4netns v0.4.2/go.mod h1:iOY28IeFVqFHnK0qkBCQ3eKzKQgSW5DtlXFQJyJMAQk= +github.com/rootless-containers/rootlesskit/v2 v2.3.5 h1:WGY05oHE7xQpSkCGfYP9lMY5z19tCxA8PhWlvP1cKx8= +github.com/rootless-containers/rootlesskit/v2 v2.3.5/go.mod h1:83EIYLeMX8UeNgLHkR1PefoSV76aKEC+OyI3vzrEfvw= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smallstep/pkcs7 v0.1.1 h1:x+rPdt2W088V9Vkjho4KtoggyktZJlMduZAtRHm68LU= +github.com/smallstep/pkcs7 v0.1.1/go.mod h1:dL6j5AIz9GHjVEBTXtW+QliALcgM19RtXaTeyxI+AfA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw= +github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= +github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= +github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= +github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= +github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= +github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= +github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yuchanns/srslog v1.1.0 h1:CEm97Xxxd8XpJThE0gc/XsqUGgPufh5u5MUjC27/KOk= +github.com/yuchanns/srslog v1.1.0/go.mod h1:HsLjdv3XV02C3kgBW2bTyW6i88OQE+VYJZIxrPKPPak= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= +go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= +golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw= -google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= +google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -216,27 +594,38 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= -k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= +k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= -k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= -k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak= -k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= -k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= -k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= -k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk= -k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w= +k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= +k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8= +k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc= +k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= +k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k= +k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI= +k8s.io/cri-api v0.32.3 h1:E8VXbXNn4yAgmuKTeNzg0C1MFSxzTdlHSwUvjuYlPTY= +k8s.io/cri-api v0.32.3/go.mod h1:DCzMuTh2padoinefWME0G678Mc3QFbLMF2vEweGzBAI= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU= @@ -247,3 +636,7 @@ sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aN sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc= +tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0= +tags.cncf.io/container-device-interface/specs-go v1.0.0 h1:8gLw29hH1ZQP9K1YtAzpvkHCjjyIxHZYzBAvlQ+0vD8= +tags.cncf.io/container-device-interface/specs-go v1.0.0/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ= diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 691e09bacec4..51e9c63b67e9 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -3,6 +3,16 @@ package commit import ( "context" "fmt" + "log" + "io" + "github.com/containerd/containerd/v2/client" + "github.com/containerd/containerd/v2/pkg/namespaces" + "github.com/containerd/containerd/v2/pkg/oci" + "github.com/containerd/nerdctl/v2/pkg/api/types" + "github.com/containerd/nerdctl/v2/pkg/cmd/container" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" ) type Committer interface { @@ -10,12 +20,156 @@ type Committer interface { } type CommitterImpl struct { - // Client *containerd.Client + runtimeServiceClient runtimeapi.RuntimeServiceClient // CRI client + containerdClient *client.Client // containerd client } +// NewCommitter new a CommitterImpl +func NewCommitter() (Committer, error) { + // create gRPC connection + conn, err := grpc.NewClient(DefaultContainerdAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + + // create Containerd client: default namespace in const.go + containerdClient, err := client.NewWithConn(conn, client.WithDefaultNamespace(DefaultNamespace)) + if err != nil { + return nil, err + } + + // create CRI client + runtimeServiceClient := runtimeapi.NewRuntimeServiceClient(conn) + + return &CommitterImpl{ + runtimeServiceClient: runtimeServiceClient, + containerdClient: containerdClient, + }, nil +} + +// CreateContainer create container +func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, contentID string, baseImage string) (string, error) { + fmt.Println("========>>>> create container", devboxName, contentID, baseImage) + // 1. get image + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + image, err := c.containerdClient.GetImage(ctx, baseImage) + if err != nil { + // image not found, try to pull + log.Printf("Image %s not found, pulling...", baseImage) + image, err = c.containerdClient.Pull(ctx, baseImage, client.WithPullUnpack) + if err != nil { + return "", fmt.Errorf("failed to pull image %s: %v", baseImage, err) + } + } + + // 2. create container + // add annotations/labels + annotations := map[string]string{ + AnnotationKeyContentID: contentID, + AnnotationKeyNamespace: DefaultNamespace, + AnnotationKeyImageName: baseImage, + } + + containerName := fmt.Sprintf("%s-container", devboxName) // container name + + container, err := c.containerdClient.NewContainer(ctx, containerName, + client.WithImage(image), + client.WithNewSnapshot(containerName, image), + client.WithContainerLabels(annotations), // add annotations + client.WithNewSpec(oci.WithImageConfig(image)), // oci.WithProcessArgs("/bin/sh", "-c", "while true; do echo 'Hello, World!'; sleep 5; done"), + // oci.WithHostname("test-container"), + + ) + if err != nil { + return "", fmt.Errorf("failed to create container: %v", err) + } + + return container.ID(), nil +} + +// DeleteContainer delete container +func (c *CommitterImpl) DeleteContainer(ctx context.Context, containerName string) error { + // load container + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + container, err := c.containerdClient.LoadContainer(ctx, containerName) + if err != nil { + return fmt.Errorf("failed to load container: %v", err) + } + + // try to get and stop task + task, err := container.Task(ctx, nil) + if err == nil { + log.Printf("Stopping task for container: %s", containerName) + + // force kill task + err = task.Kill(ctx, 9) // SIGKILL + if err != nil { + log.Printf("Warning: failed to send SIGKILL: %v", err) + } else { + log.Printf("Sent SIGKILL to task") + } + + // delete task + log.Printf("Deleting task...") + _, err = task.Delete(ctx, client.WithProcessKill) + if err != nil { + log.Printf("Warning: failed to delete task: %v", err) + } else { + log.Printf("Task deleted for container: %s", containerName) + } + } + + // delete container (include snapshot) + err = container.Delete(ctx, client.WithSnapshotCleanup) + if err != nil { + return fmt.Errorf("failed to delete container: %v", err) + } + + log.Printf("Container deleted: %s", containerName) + return nil +} + +// Commit commit container to image func (c *CommitterImpl) Commit(ctx context.Context, devboxName string, contentID string, baseImage string, commitImage string) error { fmt.Println("========>>>> commit devbox", devboxName, contentID, baseImage, commitImage) - return nil + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + containerID, err := c.CreateContainer(ctx, devboxName, contentID, baseImage) + if err != nil { + return fmt.Errorf("failed to create container: %v", err) + } + + global := types.GlobalCommandOptions{ + Namespace: DefaultNamespace, + Address: DefaultContainerdAddress, + DataRoot: DefaultDataRoot, + InsecureRegistry: InsecureRegistry, + } + + opt := types.ContainerCommitOptions{ + Stdout: io.Discard, + GOptions: global, + Pause: PauseContainerDuringCommit, + DevboxOptions: types.DevboxOptions{ + RemoveBaseImageTopLayer: DevboxOptionsRemoveBaseImageTopLayer, + }, + } + return container.Commit(ctx, c.containerdClient, commitImage, containerID, opt) +} + +// GetContainerAnnotations get container annotations +func (c *CommitterImpl) GetContainerAnnotations(ctx context.Context, containerName string) (map[string]string, error) { + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + container, err := c.containerdClient.LoadContainer(ctx, containerName) + if err != nil { + return nil, fmt.Errorf("failed to load container: %v", err) + } + + // get container labels (annotations) + labels, err := container.Labels(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get container labels: %v", err) + } + return labels, nil } type GcHandler interface { @@ -23,4 +177,77 @@ type GcHandler interface { } type Handler struct { + containerdClient *client.Client +} + +func NewGcHandler(containerdClient *client.Client) GcHandler { + return &Handler{ + containerdClient: containerdClient, + } +} + +// GC gc container +func (h *Handler) GC(ctx context.Context) error { + log.Printf("Starting GC in namespace: %s", DefaultNamespace) + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + // get all container in namespace + containers, err := h.containerdClient.Containers(ctx) + if err != nil { + log.Printf("Failed to get containers, err: %v", err) + return err + } + + var deletedContainersCount int + for _, container := range containers { + // if get container's labels failed, skip + labels, err := container.Labels(ctx) + if err != nil { + log.Printf("Failed to get labels for container %s, err: %v", container.ID(), err) + continue + } + // if container is not devbox container, skip + if _, ok := labels[AnnotationKeyContentID]; !ok { + continue + } + + // get container task + task, err := container.Task(ctx, nil) + if err != nil { + // delete orphan container + log.Printf("Found Orphan Container: %s", container.ID()) + err = container.Delete(ctx, client.WithSnapshotCleanup) + if err != nil { + log.Printf("Failed to delete Orphan Container %s, err: %v", container.ID(), err) + } else { + log.Printf("Deleted Orphan Container: %s successfully", container.ID()) + deletedContainersCount++ + } + continue + } + + status, err := task.Status(ctx) + if err != nil { + log.Printf("Failed to get task status for container %s, err: %v", container.ID(), err) + continue + } + if status.Status != client.Running { + // delete task + _, err = task.Delete(ctx, client.WithProcessKill) + if err != nil { + log.Printf("Failed to delete task for container %s, err: %v", container.ID(), err) + } + + // delete container and snapshot + err = container.Delete(ctx, client.WithSnapshotCleanup) + if err != nil { + log.Printf("Failed to delete container %s, err: %v", container.ID(), err) + } else { + log.Printf("Deleted Container: %s successfully", container.ID()) + deletedContainersCount++ + } + } + + } + log.Printf("GC completed, deleted %d containers", deletedContainersCount) + return nil } diff --git a/controllers/devbox/internal/commit/commit_test.go b/controllers/devbox/internal/commit/commit_test.go new file mode 100644 index 000000000000..47c8a143f271 --- /dev/null +++ b/controllers/devbox/internal/commit/commit_test.go @@ -0,0 +1,240 @@ +package commit + +import ( + "context" + "fmt" + "sync" + "testing" + "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/containerd/containerd/v2/pkg/namespaces" +) + +// init Committer +func TestNewCommitter(t *testing.T) { + committer, err := NewCommitter() + assert.NoError(t, err) + assert.NotNil(t, committer) +} + +// test commit flow +func TestCommitFlow(t *testing.T) { + ctx := context.Background() + + // 1. create committer + committer, err := NewCommitter() + assert.NoError(t, err) + + // 2. prepare test data + devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) + contentID := "test-content-id-123" + baseImage := "docker.io/library/busybox:latest" + commitImage := fmt.Sprintf("test-devbox-commit-%d", time.Now().Unix()) + + // 3. create container and commit container + err = committer.Commit(ctx, devboxName, contentID, baseImage, commitImage) + assert.NoError(t, err) +} + +// test create container +func TestCreateContainer(t *testing.T) { + ctx := context.Background() + committer, err := NewCommitter() + assert.NoError(t, err) + + // create container + devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) + contentID := "test-content-id-456" + baseImage := "docker.io/library/nginx:latest" // use another public image to test + + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) + assert.NoError(t, err) + assert.NotEmpty(t, containerID) + + // verify container labels + annotations, err := committer.(*CommitterImpl).GetContainerAnnotations(ctx, containerID) + assert.NoError(t, err) + assert.Equal(t, contentID, annotations["devbox.sealos.io/content-id"]) +} + +// test delete container +func TestDeleteContainer(t *testing.T) { + ctx := context.Background() + committer, err := NewCommitter() + assert.NoError(t, err) + + // create a container + devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, "test-content-id-789", "docker.io/library/alpine:latest") + assert.NoError(t, err) + + // show all containers in current namespace + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + containers, err := committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + + fmt.Printf("=== All Containers in current namespace ===\n") + for _, container := range containers { + fmt.Printf("Container ID: %s\n", container.ID()) + } + fmt.Printf("=== Total %d containers ===\n", len(containers)) + + // delete container + err = committer.(*CommitterImpl).DeleteContainer(ctx, containerID) + assert.NoError(t, err) + + containers, err = committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + + fmt.Printf("=== All Containers in current namespace ===\n") + for _, container := range containers { + fmt.Printf("Container ID: %s\n", container.ID()) + } + fmt.Printf("=== Total %d containers ===\n", len(containers)) + + // verify container is deleted (try to get labels should return error) + _, err = committer.(*CommitterImpl).GetContainerAnnotations(ctx, containerID) + assert.Error(t, err) +} + +// test error cases +func TestErrorCases(t *testing.T) { + ctx := context.Background() + committer, err := NewCommitter() + assert.NoError(t, err) + + // test use not exist image to create container + _, err = committer.(*CommitterImpl).CreateContainer(ctx, "test-devbox", "test-content-id", "not-exist-image:latest") + assert.Error(t, err) + + // test use not exist container to delete + err = committer.(*CommitterImpl).DeleteContainer(ctx, "not-exist-container") + assert.Error(t, err) + + // test get not exist container label + _, err = committer.(*CommitterImpl).GetContainerAnnotations(ctx, "not-exist-container") + assert.Error(t, err) +} + +// test concurrent operations +func TestConcurrentOperations(t *testing.T) { + ctx := context.Background() + committer, err := NewCommitter() + assert.NoError(t, err) + + // concurrent to create container + containerCount := 3 + wg := sync.WaitGroup{} + wg.Add(containerCount) + var mu sync.Mutex + containers := make([]string, containerCount) + + for i := 0; i < containerCount; i++ { + go func(index int) { + defer wg.Done() + devboxName := fmt.Sprintf("test-devbox-concurrent-%d-%d", time.Now().Unix(), index) + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, + fmt.Sprintf("test-content-id-%d", index), + "docker.io/library/busybox:latest") + if err != nil { + t.Errorf("Failed to create container: %v", err) + return + } + mu.Lock() + containers = append(containers, containerID) + mu.Unlock() + }(i) + } + wg.Wait() + + // delete containers + for _, containerID := range containers { + committer.(*CommitterImpl).DeleteContainer(ctx, containerID) + } + + // get current containers list + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + currentContainers, _ := committer.(*CommitterImpl).containerdClient.Containers(ctx) + fmt.Printf("=== All Containers in current namespace ===\n") + for _, container := range currentContainers { + fmt.Printf("Container ID: %s\n", container.ID()) + } + fmt.Printf("=== Total %d containers ===\n", len(currentContainers)) + +} + +// test gc handler +func TestGCHandler(t *testing.T) { + // new Committer and GCHandler + committer, err := NewCommitter() + require.NoError(t, err) + gcHandler := NewGcHandler(committer.(*CommitterImpl).containerdClient) + + // create 5 containers + testCountainers := []struct { + devboxName string + contentID string + baseImage string + }{ + {"test-gc-devbox-1", "test-gc-content-id-1", "docker.io/library/busybox:latest"}, + {"test-gc-devbox-2", "test-gc-content-id-2", "docker.io/library/nginx:latest"}, + {"test-gc-devbox-3", "test-gc-content-id-3", "docker.io/library/alpine:latest"}, + {"test-gc-devbox-4", "test-gc-content-id-4", "docker.io/library/busybox:latest"}, + {"test-gc-devbox-5", "test-gc-content-id-5", "docker.io/library/nginx:latest"}, + } + + for _, container := range testCountainers { + _, err := committer.(*CommitterImpl).CreateContainer(context.Background(), container.devboxName, container.contentID, container.baseImage) + require.NoError(t, err) + } + + // show all containers before gc + ctx := namespaces.WithNamespace(context.Background(), DefaultNamespace) + currentContainers, _ := committer.(*CommitterImpl).containerdClient.Containers(ctx) + fmt.Printf("=== All Containers before gc ===\n") + for _, container := range currentContainers { + fmt.Printf("Container ID: %s\n", container.ID()) + } + fmt.Printf("=== Total %d containers ===\n", len(currentContainers)) + + // gc: + err = gcHandler.GC(context.Background()) + require.NoError(t, err) + + // verify containers are deleted + // get current containers list + afterContainers, _ := committer.(*CommitterImpl).containerdClient.Containers(ctx) + fmt.Printf("=== All Containers after gc ===\n") + for _, container := range afterContainers { + fmt.Printf("Container ID: %s\n", container.ID()) + } + fmt.Printf("=== Total %d containers ===\n", len(afterContainers)) +} + +// // test large image +// func TestLargeImage(t *testing.T) { +// ctx := context.Background() +// committer, err := NewCommitter() +// assert.NoError(t, err) + +// devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) +// contentID := "test-content-id-large" +// baseImage := "docker.io/library/tensorflow/tensorflow:latest" // large image +// commitImage := fmt.Sprintf("localhost:5000/test-large-commit-%d:latest", time.Now().Unix()) + +// // create container +// containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) +// if err != nil { +// t.Logf("Skip large image test due to error: %v", err) +// t.Skip() +// } + +// // commit container +// err = committer.Commit(ctx, devboxName, contentID, baseImage, commitImage) +// assert.NoError(t, err) + +// // clean up +// err = committer.(*CommitterImpl).DeleteContainer(ctx, containerID) +// assert.NoError(t, err) +// } diff --git a/controllers/devbox/internal/commit/const.go b/controllers/devbox/internal/commit/const.go new file mode 100644 index 000000000000..923f2495a002 --- /dev/null +++ b/controllers/devbox/internal/commit/const.go @@ -0,0 +1,14 @@ +package commit + +const ( + DefaultNamespace = "sealos.io" + DefaultContainerdAddress = "unix:///var/run/containerd/containerd.sock" + DefaultDataRoot = "/var/lib/containerd" + InsecureRegistry = true + PauseContainerDuringCommit = false + + AnnotationKeyContentID = "devbox.sealos.io/content-id" + AnnotationKeyNamespace = "namespace" + AnnotationKeyImageName = "image.name" + DevboxOptionsRemoveBaseImageTopLayer = true +) diff --git a/controllers/go.work.sum b/controllers/go.work.sum index 03baad967dec..1f7800b2a4bc 100644 --- a/controllers/go.work.sum +++ b/controllers/go.work.sum @@ -1,6 +1,7 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= +cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= @@ -414,6 +415,7 @@ cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxB cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0 h1:jXIpfcH/VYSE1SYcPzO0n1VVb+sAamiLOgCw45JbOQk= @@ -1655,13 +1657,16 @@ cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e/go.mod h1:ApHc cuelang.org/go v0.8.0 h1:fO1XPe/SUGtc7dhnGnTPbpIDoQm/XxhDtoSF7jzO01c= cuelang.org/go v0.8.0/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06fa2la1/H/Ho= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1 h1:LNhjNn8DerC8f9DHLz6lS0YYul/b602DUxDgGkd/Aik= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -1712,9 +1717,11 @@ github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20O github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= +github.com/KarpelesLab/reflink v1.0.1/go.mod h1:WGkTOKNjd1FsJKBw3mu4JvrPEDJyJJ+JPtxBkbPoCok= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= @@ -1730,7 +1737,9 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8 github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Masterminds/vcs v1.13.3/go.mod h1:TiE7xuEjl1N4j016moRd6vezp6e6Lz23gypeXfzXeW8= github.com/Microsoft/cosesign1go v0.0.1/go.mod h1:fj1svfAxQeQNJ2SLaQu8mHx2rtPIsloZl065GqLF3io= +github.com/Microsoft/cosesign1go v1.4.0/go.mod h1:1La/HcGw19rRLhPW0S6u55K6LKfti+GQSgGCtrfhVe8= github.com/Microsoft/didx509go v0.0.2/go.mod h1:F+msvNlKCEm3RgUE3kRpi7E+6hdR6r5PtOLWQKYfGbs= +github.com/Microsoft/didx509go v0.0.3/go.mod h1:wWt+iQsLzn3011+VfESzznLIp/Owhuj7rLF7yLglYbk= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= @@ -1785,6 +1794,7 @@ github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412/go.mod h1:AI9hp1tkp10pAlK5TCwL+7yWbRgtDm9jhToq6qij2xs= github.com/ahmetb/gen-crd-api-reference-docs v0.3.0 h1:+XfOU14S4bGuwyvCijJwhhBIjYN+YXS18jrCY2EzJaY= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM= @@ -1813,8 +1823,10 @@ github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPp github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= github.com/alexflint/go-filemutex v1.2.0/go.mod h1:mYyQSWvw9Tx2/H2n9qXPb52tTYfE0pZAWcBq5mK025c= +github.com/alexflint/go-filemutex v1.3.0/go.mod h1:U0+VA/i30mGBlLCrFPGtTe9y6wGQfNAWPBTekHQ+c8A= github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U= github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -1861,8 +1873,27 @@ github.com/aws/aws-sdk-go v1.44.257/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8 github.com/aws/aws-sdk-go v1.50.8 h1:gY0WoOW+/Wz6XmYSgDH9ge3wnAevYDSQWPxxJvqAkP4= github.com/aws/aws-sdk-go v1.50.8/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.9.1 h1:ZbovGV/qo40nrOJ4q8G33AGICzaPI45FHQWJ9650pF4= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8/go.mod h1:WPv2FRnkIOoDv/8j2gSUsI4qDc7392w5anFB/I89GZ8= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1 h1:w/fPGB0t5rWwA43mux4e9ozFSH5zF1moQemlA131PWc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= +github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= github.com/aws/smithy-go v1.8.0 h1:AEwwwXQZtUwP5Mz506FeXXrKBe0jA8gVM+1gEcSRooc= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd h1:jZtX5jh5IOMu0fpOTC3ayh6QGSPJ/KWOv1lgPvbRw1M= github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542 h1:nYXb+3jF6Oq/j8R/y90XrKpreCxIalBWfeyeKymgOPk= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= @@ -1922,10 +1953,13 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/charmbracelet/keygen v0.5.1/go.mod h1:zznJVmK/GWB6dAtjluqn2qsttiCBhA5MZSiwb80fcHw= +github.com/checkpoint-restore/checkpointctl v1.3.0/go.mod h1:dqZH4wDvbjnsqFGK2LdUDk21yFQ1dCAtzgRMlG44KDM= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/checkpoint-restore/go-criu/v6 v6.3.0/go.mod h1:rrRTN/uSwY2X+BPRl/gkulo9gsKOSAeVp9/K2tv7xZI= +github.com/checkpoint-restore/go-criu/v7 v7.2.0/go.mod h1:u0LCWLg0w4yqqu14aXhiB4YD3a1qd8EcCEg7vda5dwo= github.com/chmduquesne/rollinghash v4.0.0+incompatible/go.mod h1:Uc2I36RRfTAf7Dge82bi3RU0OQUmXT9iweIcPqvr8A0= github.com/chromedp/cdproto v0.0.0-20220321060548-7bc2623472b3/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0= github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= @@ -1973,6 +2007,7 @@ github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -2010,6 +2045,7 @@ github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4q github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -2035,6 +2071,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/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= +github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= +github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -2070,14 +2108,13 @@ github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo= github.com/containerd/imgcrypt v1.1.7/go.mod h1:FD8gqIcX5aTotCtOmjeCsi3A1dHmTZpnMISGKSczt4k= github.com/containerd/imgcrypt v1.1.8/go.mod h1:x6QvFIkMyO2qGIY2zXc88ivEzcbgvLdWjoZyGqDap5U= -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.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/nri v0.3.0/go.mod h1:Zw9q2lP16sdg0zYybemZ9yTDy8g7fPCIB3KXOGlggXI= github.com/containerd/nri v0.4.0/go.mod h1:Zw9q2lP16sdg0zYybemZ9yTDy8g7fPCIB3KXOGlggXI= github.com/containerd/nri v0.8.0/go.mod h1:uSkgBrCdEtAiEz4vnrq8gmAC4EnVAM5Klt0OuK5rZYQ= +github.com/containerd/otelttrpc v0.1.0/go.mod h1:XhoA2VvaGPW1clB2ULwrBZfXVuEWuyOd2NUD1IM0yTg= github.com/containerd/protobuild v0.3.0/go.mod h1:5mNMFKKAwCIAkFBPiOdtRx2KiQlyEJeMXnL5R1DsWu8= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= @@ -2099,6 +2136,7 @@ github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1Dv github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= github.com/containerd/zfs v1.1.0/go.mod h1:oZF9wBnrnQjpWLaPKEinrx3TQ9a+W/RJO7Zb41d8YLE= +github.com/containerd/zfs/v2 v2.0.0-rc.0/go.mod h1:g36g/XCEGDRxUXIFdM3oWAEvmTvhfz/eKWElqg4Secw= github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= @@ -2147,8 +2185,6 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= -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/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -2162,6 +2198,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH 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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -2173,12 +2210,15 @@ github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2 github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.3.5/go.mod h1:edhVd3c6OXKjUmSrVa/tGJRS9joFTxlslFCAyaxigkE= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= +github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= +github.com/data-accelerator/zdfs v0.1.5/go.mod h1:/MyNTsQHHKVLznaRBz+PivhIDwglu+wuXKoZxUNmLKI= github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= github.com/deckarep/golang-set/v2 v2.3.1 h1:vjmkvJt/IV27WXPyYQpAh4bRyWJc5Y435D17XQ9QU5A= github.com/deckarep/golang-set/v2 v2.3.1/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= @@ -2217,6 +2257,7 @@ github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v23.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -2225,6 +2266,7 @@ github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= @@ -2234,6 +2276,8 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-connections v0.4.1-0.20190612165340-fd1b1942c4d5 h1:2o8D0hdBky229bNnc7a8bAZkeVMpH4qsp2Rmt4g/+Zk= github.com/docker/go-connections v0.4.1-0.20190612165340-fd1b1942c4d5/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= @@ -2273,6 +2317,9 @@ github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go. github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= @@ -2282,6 +2329,7 @@ github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87K github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= @@ -2299,6 +2347,7 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -2319,6 +2368,7 @@ github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2 h1:cZqz+yOJ/R64LcKj github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/freddierice/go-losetup v0.0.0-20220711213114-2a14873012db/go.mod h1:pwuQfHWn6j2Fpl2AWw/bPLlKfojHxIIEa5TeKIgDFW4= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= @@ -2351,7 +2401,9 @@ github.com/go-gorp/gorp/v3 v3.0.5 h1:PUjzYdYu3HBOh8LE+UUmRG2P0IRDak9XMeGNvaeq4Ow github.com/go-gorp/gorp/v3 v3.0.5/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.66.6/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= @@ -2400,13 +2452,13 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-redis/redis v6.14.2+incompatible h1:UE9pLhzmWf+xHNmZsoccjXosPicuiNaInPgym8nzfg0= github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= @@ -2431,9 +2483,9 @@ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6 github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -2454,6 +2506,7 @@ github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= @@ -2481,6 +2534,7 @@ github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayf github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= +github.com/google/go-containerregistry v0.20.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM= github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y= github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= @@ -2503,6 +2557,7 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/readahead v0.0.0-20161222183148-eaceba169032/go.mod h1:qYysrqQXuV4tzsizt4oOQ6mrBZQ0xnQXP3ylXX8Jk5Y= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= @@ -2564,6 +2619,7 @@ github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -2583,6 +2639,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaW github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99 h1:JYghRBlGCZyCF2wNUJ8W0cwaQdtpcssJ4CgC406g+WU= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= @@ -2594,7 +2652,10 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjd github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= github.com/hanwen/go-fuse/v2 v2.1.1-0.20220112183258-f57e95bda82d/go.mod h1:B1nGE/6RBFyBRC1RRnf23UpwCdyJ31eukw34oAKukAc= +github.com/hanwen/go-fuse/v2 v2.6.3/go.mod h1:ugNaD/iv5JYyS1Rcvi57Wz7/vrLQJo10mmketmoef48= github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.10.1 h1:MwZJp86nlnL+6+W1Zly4JUuVn9YHhMggBirMpHGD7kw= @@ -2604,8 +2665,6 @@ github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPT github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-getter v1.6.2/go.mod h1:IZCrswsZPeWv9IkVnLElzRU/gz/QPi6pZHn4tv6vbwA= @@ -2621,12 +2680,11 @@ github.com/hashicorp/go-kms-wrapping/v2 v2.0.8/go.mod h1:qTCjxGig/kjuj3hk1z8pOUr github.com/hashicorp/go-memdb v1.3.2 h1:RBKHOsnSszpU6vxq80LzC2BaQjuuvoyaQbkLTf7V7g8= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3 h1:QlWt0KvWT0lq8MFppF9tsJGF+ynG7ztc2KIPhzRGk7s= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= @@ -2697,9 +2755,11 @@ github.com/imdario/mergo v0.3.14/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+h github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= +github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k= github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= github.com/intel/goresctrl v0.3.0/go.mod h1:fdz3mD85cmP9sHD8JUlrNWAxvwM86CrbmVXltEKd7zk= github.com/intel/goresctrl v0.5.0/go.mod h1:mIe63ggylWYr0cU/l8n11FAkesqfvuP3oktIsxvu0T0= +github.com/intel/goresctrl v0.8.0/go.mod h1:T3ZZnuHSNouwELB5wvOoUJaB7l/4Rm23rJy/wuWJlr0= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= @@ -2778,7 +2838,6 @@ github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyP github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= github.com/kubernetes-csi/external-snapshotter/client/v6 v6.2.0 h1:cMM5AB37e9aRGjErygVT6EuBPB6s5a+l95OPERmSlVM= github.com/kubernetes-csi/external-snapshotter/client/v6 v6.2.0/go.mod h1:VQVLCPGDX5l6V5PezjlDXLa+SpCbWSVU7B16cFWVVeE= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -2788,9 +2847,11 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/jwx v1.2.26/go.mod h1:MaiCdGbn3/cckbOFSCluJlJMmp9dmZm5hDuIkx8ftpQ= +github.com/lestrrat-go/jwx v1.2.29/go.mod h1:hU8k2l6WF0ncx20uQdOmik/Gjg6E3/wIRtXSNFeZuB8= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/letsencrypt/boulder v0.0.0-20230213213521-fdfea0d469b6/go.mod h1:PUgW5vI9ANEaV6qv9a6EKu8gAySgwf0xrzG9xIB/CK0= @@ -2810,6 +2871,7 @@ github.com/lyft/protoc-gen-star/v2 v2.0.1 h1:keaAo8hRuAT0O3DfJ/wM3rufbAjGeJ1lAtW github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/lyft/protoc-gen-star/v2 v2.0.3 h1:/3+/2sWyXeMLzKd1bX+ixWKgEMsULrIivpDsuaF441o= github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= +github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= @@ -2851,8 +2913,6 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= @@ -2862,6 +2922,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0/go.mod h1:fJ0UAZc1fx3xZhU4eSHQDJ1ApFmTVhp5VTpV9tm2ogg= +github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= +github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= @@ -2869,7 +2931,6 @@ github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= @@ -2888,7 +2949,6 @@ github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFW github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= @@ -2904,9 +2964,6 @@ github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= -github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= @@ -2914,22 +2971,25 @@ github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVO github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/vpnkit v0.6.0/go.mod h1:CNuEpfSK4ZY/NKFWD5M79GUZcYFydh81XQ2GZnT44cQ= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= @@ -2945,6 +3005,7 @@ github.com/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy github.com/nelsam/hel/v2 v2.3.3/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/networkplumbing/go-nft v0.2.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs= github.com/networkplumbing/go-nft v0.3.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs= +github.com/networkplumbing/go-nft v0.4.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= @@ -2981,6 +3042,7 @@ github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -3009,14 +3071,14 @@ github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16A github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/open-policy-agent/opa v0.42.2/go.mod h1:MrmoTi/BsKWT58kXlVayBb+rYVeaMwuBm3nYAN3923s= +github.com/open-policy-agent/opa v0.70.0/go.mod h1:Y/nm5NY0BX0BqjBriKUiV81sCl8XOjjvqQG7dXrggtI= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= @@ -3036,21 +3098,21 @@ github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6i github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runc v1.1.8/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= +github.com/opencontainers/runc v1.2.3 h1:fxE7amCzfZflJO2lHXf4y/y8M1BoAqp+FVmG19oYB80= +github.com/opencontainers/runc v1.2.3/go.mod h1:nSxcWUydXrsBZVYNSkTjoQ/N6rcyTtn+1SD5D4+kRIM= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI= github.com/opencontainers/runtime-tools v0.9.1-0.20230317050512-e931285f4b69/go.mod h1:bNpfuSHA3DZRtD0TPWO8LzgtLpFPTVA/3jDkzD/OPyk= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= @@ -3088,6 +3150,7 @@ github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9F github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= @@ -3097,6 +3160,7 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/poy/onpar v0.0.0-20200406201722-06f95a1c68e8/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= @@ -3105,7 +3169,6 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prY github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/proglottis/gpgme v0.1.3/go.mod h1:fPbW/EZ0LvwQtH8Hy7eixhp1eF3G39dtx7GUN+0Gmy0= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.71.0 h1:et+XkusxWLz+XNqZiyMom9tv9ACvNAUyLXti2LTiV7o= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.71.0/go.mod h1:3RiUkFmR9kmPZi9r/8a5jw0a9yg+LMmr7qa0wjqvSiI= @@ -3120,6 +3183,7 @@ github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQg github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= @@ -3166,6 +3230,8 @@ github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfm github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rubenv/sql-migrate v1.3.1 h1:Vx+n4Du8X8VTYuXbhNxdEUoh6wiJERA0GlWocR5FrbA= github.com/rubenv/sql-migrate v1.3.1/go.mod h1:YzG/Vh82CwyhTFXy+Mf5ahAiiEOpAlHurg+23VEzcsk= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -3180,9 +3246,11 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/safchain/ethtool v0.5.10/go.mod h1:w9jh2Lx7YBR4UwzLkzCmWl85UY0W2uZdd7/DckVE5+c= github.com/sagikazarmark/crypt v0.10.0 h1:96E1qrToLBU6fGzo+PRRz7KGOc9FkYFiPnR3/zf8Smg= github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= github.com/sanity-io/litter v1.5.4/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= @@ -3224,9 +3292,9 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= @@ -3254,6 +3322,7 @@ github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUq 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/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -3268,15 +3337,14 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec h1:q6XVwXmKvCRHRqesF3cSv6lNqqHi0QWOvgDlSohg8UA= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e h1:mOtuXaRAbVZsxAHVdPR3IjfmN8T1h2iczJLynhLybf8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -3296,6 +3364,7 @@ github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= github.com/tg123/go-htpasswd v1.2.0/go.mod h1:h7IzlfpvIWnVJhNZ0nQ9HaFxHb7pn5uFJYLlEUJa2sM= github.com/theupdateframework/go-tuf v0.5.2/go.mod h1:SyMV5kg5n4uEclsyxXJZI2UxPFJNDc4Y+r7wv+MlvTA= @@ -3312,19 +3381,21 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7 github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= +github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83 h1:9AUN7+NK4IV+A11igqjQM5i8obiOAQo4SXgjaxe+orI= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= +github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= @@ -3336,12 +3407,14 @@ github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8ok github.com/vbauerster/mpb/v8 v8.4.0/go.mod h1:vjp3hSTuCtR+x98/+2vW3eZ8XzxvGoP8CPseHMhiPyc= github.com/vektah/gqlparser/v2 v2.4.5/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0= github.com/veraison/go-cose v1.0.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= +github.com/veraison/go-cose v1.1.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netlink v1.3.1-0.20250303224720-0e7078ed04c8/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= @@ -3352,24 +3425,16 @@ github.com/vladimirvivien/gexe v0.1.1/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6e github.com/vmware-tanzu/crash-diagnostics v0.3.7/go.mod h1:gO8670rd+qdjnJVol674snT/A46GQ27u085kKhZznlM= github.com/vmware-tanzu/velero v1.10.1 h1:6WYOolZIygHb8FOZtpp8vCqCuy5Mk3qBF1S65L5cjuo= github.com/vmware-tanzu/velero v1.10.1/go.mod h1:N0J+j8xGSmanGpy1zCRMH2DMGPpwkUj9EZIUXfOlanY= +github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810/go.mod h1:dF0BBJ2YrV1+2eAIyEI+KeSidgA6HqoIP1u5XTlMq/o= github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b h1:0Ve0/CCjiAiyKddUMUn3RwIGlq2iTW4GuVzyoKBYO/8= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc= github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= -github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= -github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -3377,7 +3442,9 @@ github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= +github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= @@ -3395,6 +3462,7 @@ github.com/zalando/go-keyring v0.2.1/go.mod h1:g63M2PPn0w5vjmEbwAX3ib5I+41zdm4es github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= @@ -3408,6 +3476,7 @@ go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= @@ -3419,6 +3488,7 @@ go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+ go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k= go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28= +go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4= go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= @@ -3429,6 +3499,7 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4 go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0= go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E= +go.etcd.io/etcd/client/pkg/v3 v3.5.17/go.mod h1:4DqK1TKacp/86nJk4FLQqo6Mn2vvQFBmruW3pP14H/w= go.etcd.io/etcd/client/v2 v2.305.4 h1:Dcx3/MYyfKcPNLpR4VVQUP5KgYrBeJtktBwEKkw08Ao= go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU= go.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4= @@ -3447,6 +3518,7 @@ go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQa go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao= go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50= +go.etcd.io/etcd/client/v3 v3.5.17/go.mod h1:j2d4eXTHWkT2ClBgnnEPm/Wuu7jsqku41v9DZ3OtjQo= go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= go.etcd.io/etcd/pkg/v3 v3.5.4 h1:V5Dvl7S39ZDwjkKqJG2BfXgxZ3QREqqKifWQgIw5IM0= go.etcd.io/etcd/pkg/v3 v3.5.4/go.mod h1:OI+TtO+Aa3nhQSppMbwE4ld3uF1/fqqwbpfndbbrEe0= @@ -3478,13 +3550,15 @@ go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bs go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s= go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= +go.etcd.io/gofail v0.2.0/go.mod h1:nL3ILMGfkXTekKI3clMBNazKnjUZjYLKmBHzsVAnC1o= go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.mozilla.org/pkcs7 v0.9.0/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= +go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 h1:sO4WKdPAudZGKPcpZT4MJn6JaDmpyLrMPDGGyA1SttE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E= @@ -3501,6 +3575,7 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 h1:Q3C9yzW6I9jqEc8sawxzxZmY48fs9u220KXq6d5s3XU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0 h1:0BgiNWjN7rUWO9HdjF4L12r8OW86QkVQcYmCjnayJLo= @@ -3514,6 +3589,7 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfa go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= @@ -3534,6 +3610,8 @@ go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZV go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFufObyB0= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= @@ -3552,6 +3630,7 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0/go.mod h1:E+/KKhwOSw8yoPxSSuUHG6vKppkvhN+S1Jc7Nib3k3o= @@ -3561,9 +3640,11 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0/go.mod h go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0/go.mod h1:+N7zNjIJv4K+DeX67XXET0P+eIciESgaFDBqh+ZJFS4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0 h1:OiYdrCq1Ctwnovp6EofSPwlp5aGy4LgKNbkg7PtEUw8= go.opentelemetry.io/otel/internal/metric v0.25.0 h1:w/7RXe16WdPylaIXDgcYM6t/q0K5lXgSdZOEbIEyliE= go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= @@ -3582,6 +3663,8 @@ go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xC go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8= @@ -3600,10 +3683,13 @@ go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZ go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= @@ -3624,6 +3710,8 @@ go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40 go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -3632,6 +3720,7 @@ go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJP go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -3650,6 +3739,7 @@ go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -3696,9 +3786,12 @@ golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= @@ -3732,11 +3825,14 @@ golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -3788,6 +3884,10 @@ golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -3822,13 +3922,18 @@ golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2 golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -3841,7 +3946,6 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -3872,7 +3976,6 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -3902,7 +4005,6 @@ golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -3912,9 +4014,7 @@ golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -3923,8 +4023,12 @@ golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -3950,6 +4054,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -4001,8 +4107,11 @@ golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= @@ -4270,6 +4379,9 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go. google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas= google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= +google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc h1:g3hIDl0jRNd9PPTs2uBzYuaD5mQuwOkZY0vSc0LR32o= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= @@ -4324,6 +4436,10 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240823204242-4ba0660f739c/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -4373,15 +4489,24 @@ google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJai google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= +google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20/go.mod h1:Nr5H8+MlGWr5+xX/STzdoEqJrO+YteqFbMyCsrb6mH0= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -4408,7 +4533,6 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= @@ -4434,6 +4558,7 @@ k8s.io/apiserver v0.28.1/go.mod h1:d8aizlSRB6yRgJ6PKfDkdwCy2DXt/d1FDR6iJN9kY1w= k8s.io/apiserver v0.28.3/go.mod h1:YIpM+9wngNAv8Ctt0rHG4vQuX/I5rvkEMtZtsxW2rNM= k8s.io/apiserver v0.29.0 h1:Y1xEMjJkP+BIi0GSEv1BBrf1jLU9UPfAnnGGbbDdp7o= k8s.io/apiserver v0.29.0/go.mod h1:31n78PsRKPmfpee7/l9NYEv67u6hOL6AfcE761HapDM= +k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= k8s.io/cli-runtime v0.28.2 h1:64meB2fDj10/ThIMEJLO29a1oujSm0GQmKzh1RtA/uk= k8s.io/cli-runtime v0.28.2/go.mod h1:bTpGOvpdsPtDKoyfG4EG041WIyFZLV9qq4rPlkyYfDA= k8s.io/cli-runtime v0.28.3 h1:lvuJYVkwCqHEvpS6KuTZsUVwPePFjBfSGvuaLl2SxzA= @@ -4447,6 +4572,8 @@ k8s.io/code-generator v0.28.2/go.mod h1:ueeSJZJ61NHBa0ccWLey6mwawum25vX61nRZ6WOz k8s.io/code-generator v0.28.3 h1:I847QvdpYx7xKiG2KVQeCSyNF/xU9TowaDAg601mvlw= k8s.io/code-generator v0.28.3/go.mod h1:A2EAHTRYvCvBrb/MM2zZBNipeCk3f8NtpdNIKawC43M= k8s.io/code-generator v0.32.1/go.mod h1:zaILfm00CVyP/6/pJMJ3zxRepXkxyDfUV5SNG4CjZI4= +k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk= +k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w= k8s.io/component-helpers v0.28.2 h1:r/XJ265PMirW9EcGXr/F+2yWrLPo2I69KdvcY/h9HAo= k8s.io/component-helpers v0.28.2/go.mod h1:pF1R5YWQ+sgf0i6EbVm+MQCzkYuqutDUibdrkvAa6aI= k8s.io/component-helpers v0.28.3 h1:te9ieTGzcztVktUs92X53P6BamAoP73MK0qQP0WmDqc= @@ -4493,6 +4620,7 @@ k8s.io/kms v0.29.0/go.mod h1:mB0f9HLxRXeXUfHfn1A7rpwOlzXI1gIWu86z6buNoYA= k8s.io/kms v0.30.1 h1:gEIbEeCbFiaN2tNfp/EUhFdGr5/CSj8Eyq6Mkr7cCiY= k8s.io/kms v0.30.1/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4= k8s.io/kms v0.32.1/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= +k8s.io/kms v0.32.3/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= k8s.io/kube-aggregator v0.19.12/go.mod h1:K76wPd03pSHEmS1FgJOcpryac5C3va4cbCvSu+4EmE0= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= @@ -4503,6 +4631,7 @@ k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d/go.mod h1:AsvuZPBlUDVuCdz k8s.io/kubectl v0.28.2/go.mod h1:6EQWTPySF1fn7yKoQZHYf9TPwIl2AygHEcJoxFekr64= k8s.io/kubelet v0.26.1 h1:wQyCQYmLW6GN3v7gVTxnc3jAE4zMYDlzdF3FZV4rKas= k8s.io/kubelet v0.26.1/go.mod h1:gFVZ1Ab4XdjtnYdVRATwGwku7FhTxo6LVEZwYoQaDT8= +k8s.io/kubelet v0.32.3/go.mod h1:yyAQSCKC+tjSlaFw4HQG7Jein+vo+GeKBGdXdQGvL1U= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/kubernetes v1.28.3 h1:XTci6gzk+JR51UZuZQCFJ4CsyUkfivSjLI4O1P9z6LY= k8s.io/kubernetes v1.28.3/go.mod h1:NhAysZWvHtNcJFFHic87ofxQN7loylCQwg3ZvXVDbag= @@ -4594,6 +4723,7 @@ nullprogram.com/x/optparse v1.0.0 h1:xGFgVi5ZaWOnYdac2foDT3vg0ZZC9ErXFV57mr4OHrI oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= oras.land/oras-go v1.2.4 h1:djpBY2/2Cs1PV87GSJlxv4voajVOMZxqqtq9AB8YNvY= oras.land/oras-go v1.2.4/go.mod h1:DYcGfb3YF1nKjcezfX2SNlDAeQFKSXmf+qrFmrh4324= +oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg= periph.io/x/host/v3 v3.8.0/go.mod h1:rzOLH+2g9bhc6pWZrkCrmytD4igwQ2vxFw6Wn6ZOlLY= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= @@ -4611,6 +4741,7 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/ sigs.k8s.io/controller-tools v0.6.2 h1:+Y8L0UsAugDipGRw8lrkPoAi6XqlQVZuf1DQHME3PgU= sigs.k8s.io/gateway-api v0.4.0 h1:07IJkTt21NetZTHtPKJk2I4XIgDN4BAlTIq1wK7V11o= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/knftables v0.0.18/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3/go.mod h1:/d88dHCvoy7d0AKFT0yytezSGZKjsZBVs9YTkBHSGFk= diff --git a/service/go.work.sum b/service/go.work.sum index 446528fccd27..af5aa9e51e0b 100644 --- a/service/go.work.sum +++ b/service/go.work.sum @@ -897,6 +897,7 @@ github.com/bytedance/sonic/loader v0.2.2/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFos github.com/casbin/casbin v1.7.0 h1:PuzlE8w0JBg/DhIqnkF1Dewf3z+qmUZMVN07PonvVUQ= github.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= @@ -1476,6 +1477,14 @@ github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJV github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0 h1:QIF48X1cihydXibm+4wfAc0r/qyPyuFiPFRNphdMpEE= github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400 h1:091wFNQB3PXcL5+me0joH7EiyqQaI0wGMpEjVCkK04U= github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d h1:NVwnfyR3rENtlz62bcrkXME3INVUa4lcdGt+opvxExs= +github.com/smartwalle/alipay/v3 v3.2.25 h1:cRDN+fpDWTVHnuHIF/vsJETskRXS/S+fDOdAkzXmV/Q= +github.com/smartwalle/alipay/v3 v3.2.25/go.mod h1:lVqFiupPf8YsAXaq5JXcwqnOUC2MCF+2/5vub+RlagE= +github.com/smartwalle/ncrypto v1.0.4 h1:P2rqQxDepJwgeO5ShoC+wGcK2wNJDmcdBOWAksuIgx8= +github.com/smartwalle/ncrypto v1.0.4/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk= +github.com/smartwalle/ngx v1.0.9 h1:pUXDvWRZJIHVrCKA1uZ15YwNti+5P4GuJGbpJ4WvpMw= +github.com/smartwalle/ngx v1.0.9/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0= +github.com/smartwalle/nsign v1.0.9 h1:8poAgG7zBd8HkZy9RQDwasC6XZvJpDGQWSjzL2FZL6E= +github.com/smartwalle/nsign v1.0.9/go.mod h1:eY6I4CJlyNdVMP+t6z1H6Jpd4m5/V+8xi44ufSTxXgc= github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= From 1a1d5fb8e165191f0656200305df6ab877189e6b Mon Sep 17 00:00:00 2001 From: yy Date: Tue, 29 Jul 2025 14:21:50 +0800 Subject: [PATCH 03/46] fix committer init --- controllers/devbox/cmd/main.go | 6 +++++- controllers/devbox/internal/controller/devbox_controller.go | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 8908d9e4c163..a35081835782 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -272,7 +272,11 @@ func main() { os.Exit(1) } - committer := &commit.CommitterImpl{} + committer, err := commit.NewCommitter() + if err != nil { + setupLog.Error(err, "unable to create committer") + os.Exit(1) + } stateChangeHandler := controller.StateChangeHandler{ Client: mgr.GetClient(), diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index d9c79db231d5..7581604f0eb5 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -186,13 +186,11 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } // sync devbox state - logger.Info("syncing devbox state") - if stateChanged := r.syncDevboxState(ctx, devbox); stateChanged { + if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == r.NodeName && r.syncDevboxState(ctx, devbox) { logger.Info("devbox state changed, wait for state change handler to handle the event, requeue after 5 seconds", "from", devbox.Status.State, "to", devbox.Spec.State) r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } - logger.Info("sync devbox state success") // create or update pod logger.Info("syncing pod") From 8a8043e4357caff57d0da98886886868faaf65a0 Mon Sep 17 00:00:00 2001 From: Yun Pan Date: Tue, 29 Jul 2025 14:31:23 +0800 Subject: [PATCH 04/46] Add acceptance scoring for devbox scheduling (#39) * Add acceptance scoring for devbox scheduling and implement filesystem stats provider * Fix typo in ContainerFsStats method name * Add CPU request and limit ratio fields to AcceptanceConsideration struct * Add memory request and limit ratio fields to AcceptanceConsideration struct * Add annotations and default values for container resource ratios in Devbox * Rename container filesystem threshold annotation and update related logic to use available threshold * Change acceptanceThreshold type from uint to int and update related logic --- .../devbox/api/v1alpha1/devbox_types.go | 8 + controllers/devbox/cmd/main.go | 4 + .../internal/controller/devbox_controller.go | 266 +++++++++++++++++- .../internal/controller/helper/schedule.go | 27 ++ controllers/devbox/internal/stat/stat.go | 54 ++++ 5 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 controllers/devbox/internal/controller/helper/schedule.go create mode 100644 controllers/devbox/internal/stat/stat.go diff --git a/controllers/devbox/api/v1alpha1/devbox_types.go b/controllers/devbox/api/v1alpha1/devbox_types.go index 3cc78eedfdfe..031eed27b18e 100644 --- a/controllers/devbox/api/v1alpha1/devbox_types.go +++ b/controllers/devbox/api/v1alpha1/devbox_types.go @@ -31,6 +31,14 @@ const ( AnnotationStorageLimit = "devbox.sealos.io/storage-limit" // Annotate the devbox pod with the devbox part of AnnotationContentID = "devbox.sealos.io/content-id" + // Annotate the devbox node with container filesystem threshold + AnnotationContainerFSAvailableThreshold = "devbox.sealos.io/container-fs-available-threshold" + // Annotate the devbox node with cpu request and limit ratio + AnnotationCPURequestRatio = "devbox.sealos.io/cpu-request-ratio" + AnnotationCPULimitRatio = "devbox.sealos.io/cpu-limit-ratio" + // Annotate the devbox node with memory request and limit ratio + AnnotationMemoryRequestRatio = "devbox.sealos.io/memory-request-ratio" + AnnotationMemoryLimitRatio = "devbox.sealos.io/memory-limit-ratio" // Label the devbox pod with the devbox part of LabelDevBoxPartOf = "devbox" diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index a35081835782..d3a95d2b4a6b 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -98,6 +98,7 @@ func main() { var restartPredicateDuration time.Duration // devbox node label var devboxNodeLabel string + var acceptanceThreshold int flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") @@ -130,6 +131,8 @@ func main() { flag.DurationVar(&restartPredicateDuration, "restart-predicate-duration", 2*time.Hour, "Sets the restart predicate time duration for devbox controller restart. By default, the duration is set to 2 hours.") // devbox node label flag.StringVar(&devboxNodeLabel, "devbox-node-label", "devbox.sealos.io/node", "The label of the devbox node") + // scheduling flags + flag.IntVar(&acceptanceThreshold, "acceptance-threshold", 16, "The minimum acceptance score for scheduling devbox to node. Default is 16, which means the node must have enough resources to run the devbox.") opts := zap.Options{ Development: true, } @@ -267,6 +270,7 @@ func main() { DebugMode: debugMode, RestartPredicateDuration: restartPredicateDuration, NodeName: nodes.GetNodeName(), + AcceptanceThreshold: acceptanceThreshold, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Devbox") os.Exit(1) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 7581604f0eb5..26685df3d931 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -19,12 +19,15 @@ package controller import ( "context" "fmt" + "math" + "strconv" "time" devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" "github.com/labring/sealos/controllers/devbox/internal/controller/helper" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" + "github.com/labring/sealos/controllers/devbox/internal/stat" "github.com/labring/sealos/controllers/devbox/label" "github.com/google/uuid" @@ -67,6 +70,8 @@ type DevboxReconciler struct { StateChangeRecorder record.EventRecorder RestartPredicateDuration time.Duration + AcceptanceThreshold int + stat.NodeStatsProvider } // +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxes,verbs=get;list;watch;create;update;patch;delete @@ -146,12 +151,16 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // if devbox state is running, schedule devbox to node, update devbox status and create a new commit record // and filter out the devbox that are not in the current node if devbox.Spec.State == devboxv1alpha1.DevboxStateRunning { - if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == "" { + if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == "" && r.getAcceptanceScore(ctx) >= r.AcceptanceThreshold { + // if devbox is not scheduled to node, schedule it to current node + logger.Info("devbox not scheduled to node, try scheduling to us now", + "nodeName", r.NodeName, + "contentID", devbox.Status.ContentID) // set up devbox node and content id, new a record for the devbox devbox.Status.CommitRecords[devbox.Status.ContentID].Node = r.NodeName if err := r.Status().Update(ctx, devbox); err != nil { - logger.Info("try to schedule devbox to node failed", "error", err) - return ctrl.Result{}, nil + logger.Info("try to schedule devbox to node failed. This devbox may have already been scheduled to another node", "error", err) + return ctrl.Result{}, err } logger.Info("devbox scheduled to node", "node", r.NodeName) r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox scheduled to node", "Devbox scheduled to node") @@ -570,6 +579,257 @@ func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, opts return expectPod } +func (r *DevboxReconciler) getAcceptanceConsideration(ctx context.Context) (helper.AcceptanceConsideration, error) { + logger := log.FromContext(ctx) + node := &corev1.Node{} + if err := r.Get(context.Background(), client.ObjectKey{Name: r.NodeName}, node); err != nil { + return helper.AcceptanceConsideration{}, err + } + ann := node.Annotations + ac := helper.AcceptanceConsideration{} + if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationContainerFSAvailableThreshold]); err != nil { + logger.Error(err, "failed to parse containerfs available threshold. use default value instead", "value", ann[devboxv1alpha1.AnnotationContainerFSAvailableThreshold]) + ac.ContainerFSAvailableThreshold = helper.DefaultContainerFSAvailableThreshold + } else { + ac.ContainerFSAvailableThreshold = uint(v) + } + if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationCPURequestRatio]); err != nil { + logger.Error(err, "failed to parse CPU request ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationCPURequestRatio]) + ac.CPURequestRatio = helper.DefaultCPURequestRatio + } else { + ac.CPURequestRatio = uint(v) + } + if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationCPULimitRatio]); err != nil { + logger.Error(err, "failed to parse CPU limit ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationCPULimitRatio]) + ac.CPULimitRatio = helper.DefaultCPULimitRatio + } else { + ac.CPULimitRatio = uint(v) + } + if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationMemoryRequestRatio]); err != nil { + logger.Error(err, "failed to parse memory request ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationMemoryRequestRatio]) + ac.MemoryRequestRatio = helper.DefaultMemoryRequestRatio + } else { + ac.MemoryRequestRatio = uint(v) + } + if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationMemoryLimitRatio]); err != nil { + logger.Error(err, "failed to parse memory limit ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationMemoryLimitRatio]) + ac.MemoryLimitRatio = helper.DefaultMemoryLimitRatio + } else { + ac.MemoryLimitRatio = uint(v) + } + return ac, nil +} + +func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { + logger := log.FromContext(ctx) + var ( + ac helper.AcceptanceConsideration + containerFsStats stat.FsStats + err error + availableBytes uint64 + availablePercentage uint + capacityBytes uint64 + cpuRequestPercentage uint + cpuLimitPercentage uint + memoryRequestPercentage uint + memoryLimitPercentage uint + + score int + ) + ac, err = r.getAcceptanceConsideration(ctx) + if err != nil { + logger.Error(err, "failed to get acceptance consideration") + goto unsuitable // If we can't get the acceptance consideration, we assume the node is not suitable + } + containerFsStats, err = r.ContainerFsStats(ctx) + if err != nil || containerFsStats.AvailableBytes == nil || containerFsStats.CapacityBytes == nil { + logger.Error(err, "failed to get container filesystem stats") + goto unsuitable // If we can't get the container filesystem stats, we assume the node is not suitable + } + availableBytes = *containerFsStats.AvailableBytes + capacityBytes = *containerFsStats.CapacityBytes + if minBytesRequired, ok := r.EphemeralStorage.MaximumLimit.AsInt64(); !ok { + logger.Error(err, "failed to get minimum bytes required for ephemeral storage") + goto unsuitable // If we can't get the minimum bytes required, we assume the node is not suitable + } else if availableBytes < uint64(minBytesRequired) { + logger.Info("available bytes less than minimum required", "availableBytes", availableBytes, "minimumRequired", minBytesRequired) + goto unsuitable // If available bytes are less than the minimum required, we assume the node is not suitable + } + availablePercentage = uint(float64(availableBytes) / float64(capacityBytes) * 100) + if availablePercentage > ac.ContainerFSAvailableThreshold { + logger.Info("container filesystem available percentage is greater than threshold", + "availablePercentage", availablePercentage, + "threshold", ac.ContainerFSAvailableThreshold) + score += getScoreUnit(1) + } + cpuRequestPercentage, err = r.getTotalCPURequest(ctx, r.NodeName) + if err != nil { + logger.Error(err, "failed to get total CPU request") + goto unsuitable // If we can't get the CPU request, we assume the node is not suitable + } else if cpuRequestPercentage < ac.CPURequestRatio { + logger.Info("cpu request percentage is less than cpu overcommitment request ratio", "requestPercentage", cpuRequestPercentage, "ratio", ac.CPURequestRatio) + score += getScoreUnit(0) + } + cpuLimitPercentage, err = r.getTotalCPULimit(ctx, r.NodeName) + if err != nil { + logger.Error(err, "failed to get total CPU limit") + goto unsuitable // If we can't get the CPU limit, we assume the node is not suitable + } else if cpuLimitPercentage < ac.CPULimitRatio { + logger.Info("cpu limit percentage is less than cpu overcommitment limit ratio", "limitPercentage", cpuLimitPercentage, "ratio", ac.CPULimitRatio) + score += getScoreUnit(0) + } + memoryRequestPercentage, err = r.getTotalMemoryRequest(ctx, r.NodeName) + if err != nil { + logger.Error(err, "failed to get total memory request") + goto unsuitable // If we can't get the memory request, we assume the node is not suitable + } else if memoryRequestPercentage < ac.MemoryRequestRatio { + logger.Info("memory request percentage is less than memory overcommitment request ratio", "requestPercentage", memoryRequestPercentage, "ratio", ac.MemoryRequestRatio) + score += getScoreUnit(0) + } + memoryLimitPercentage, err = r.getTotalMemoryLimit(ctx, r.NodeName) + if err != nil { + logger.Error(err, "failed to get total memory limit") + goto unsuitable // If we can't get the memory limit, we assume the node is not suitable + } else if memoryLimitPercentage < ac.MemoryLimitRatio { + logger.Info("memory limit percentage is less than memory overcommitment limit ratio", "limitPercentage", memoryLimitPercentage, "ratio", ac.MemoryLimitRatio) + score += getScoreUnit(0) + } + return score +unsuitable: + return math.MinInt +} + +// This function may lead to overflow if p is too large, but since p is always in the range of 0-6, it should be safe. +// Use with caution. +func getScoreUnit(p uint) int { + return 16 << (p * 4) +} + +// getTotalCPURequest returns the total CPU requests (in millicores) for all pods in the namespace. +func (r *DevboxReconciler) getTotalCPURequest(ctx context.Context, namespace string) (uint, error) { + podList := &corev1.PodList{} + listOpts := []client.ListOption{ + client.InNamespace(namespace), + client.MatchingFields{"spec.nodeName": r.NodeName}, + } + if err := r.List(ctx, podList, listOpts...); err != nil { + return 0, err + } + var totalCPURequest int64 + for _, pod := range podList.Items { + for _, container := range pod.Spec.Containers { + if cpuReq, ok := container.Resources.Requests[corev1.ResourceCPU]; ok { + // TODO: check if this could lead to overflow + totalCPURequest += cpuReq.MilliValue() + } + } + } + node := &corev1.Node{} + if err := r.Get(ctx, client.ObjectKey{Name: r.NodeName}, node); err != nil { + return 0, err + } + allocatableCPU := node.Status.Allocatable[corev1.ResourceCPU] + allocatableMilli := allocatableCPU.MilliValue() + if allocatableMilli == 0 { + return 0, fmt.Errorf("node %s allocatable CPU is zero", r.NodeName) + } + percentage := uint((float64(totalCPURequest) / float64(allocatableMilli)) * 100) + return percentage, nil +} + +// getTotalCPULimit returns the total CPU limits (in millicores) for all pods in the namespace. +func (r *DevboxReconciler) getTotalCPULimit(ctx context.Context, namespace string) (uint, error) { + podList := &corev1.PodList{} + listOpts := []client.ListOption{ + client.InNamespace(namespace), + client.MatchingFields{"spec.nodeName": r.NodeName}, + } + if err := r.List(ctx, podList, listOpts...); err != nil { + return 0, err + } + var totalCPULimit int64 + for _, pod := range podList.Items { + for _, container := range pod.Spec.Containers { + if cpuLimit, ok := container.Resources.Limits[corev1.ResourceCPU]; ok { + // TODO: check if this could lead to overflow + totalCPULimit += cpuLimit.MilliValue() + } + } + } + node := &corev1.Node{} + if err := r.Get(ctx, client.ObjectKey{Name: r.NodeName}, node); err != nil { + return 0, err + } + allocatableCPU := node.Status.Allocatable[corev1.ResourceCPU] + allocatableMilli := allocatableCPU.MilliValue() + if allocatableMilli == 0 { + return 0, fmt.Errorf("node %s allocatable CPU is zero", r.NodeName) + } + percentage := uint((float64(totalCPULimit) / float64(allocatableMilli)) * 100) + return percentage, nil +} + +func (r *DevboxReconciler) getTotalMemoryRequest(ctx context.Context, namespace string) (uint, error) { + podList := &corev1.PodList{} + listOpts := []client.ListOption{ + client.InNamespace(namespace), + client.MatchingFields{"spec.nodeName": r.NodeName}, + } + if err := r.List(ctx, podList, listOpts...); err != nil { + return 0, err + } + var totalMemoryRequest int64 + for _, pod := range podList.Items { + for _, container := range pod.Spec.Containers { + if memReq, ok := container.Resources.Requests[corev1.ResourceMemory]; ok { + // TODO: check if this could lead to overflow + totalMemoryRequest += memReq.Value() + } + } + } + node := &corev1.Node{} + if err := r.Get(ctx, client.ObjectKey{Name: r.NodeName}, node); err != nil { + return 0, err + } + allocatableMemory := node.Status.Allocatable[corev1.ResourceMemory] + allocatableBytes := allocatableMemory.Value() + if allocatableBytes == 0 { + return 0, fmt.Errorf("node %s allocatable memory is zero", r.NodeName) + } + percentage := uint((float64(totalMemoryRequest) / float64(allocatableBytes)) * 100) + return percentage, nil +} + +func (r *DevboxReconciler) getTotalMemoryLimit(ctx context.Context, namespace string) (uint, error) { + podList := &corev1.PodList{} + listOpts := []client.ListOption{ + client.InNamespace(namespace), + client.MatchingFields{"spec.nodeName": r.NodeName}, + } + if err := r.List(ctx, podList, listOpts...); err != nil { + return 0, err + } + var totalMemoryLimit int64 + for _, pod := range podList.Items { + for _, container := range pod.Spec.Containers { + if memLimit, ok := container.Resources.Limits[corev1.ResourceMemory]; ok { + // TODO: check if this could lead to overflow + totalMemoryLimit += memLimit.Value() + } + } + } + node := &corev1.Node{} + if err := r.Get(ctx, client.ObjectKey{Name: r.NodeName}, node); err != nil { + return 0, err + } + allocatableMemory := node.Status.Allocatable[corev1.ResourceMemory] + allocatableBytes := allocatableMemory.Value() + if allocatableBytes == 0 { + return 0, fmt.Errorf("node %s allocatable memory is zero", r.NodeName) + } + percentage := uint((float64(totalMemoryLimit) / float64(allocatableBytes)) * 100) + return percentage, nil +} type ControllerRestartPredicate struct { predicate.Funcs diff --git a/controllers/devbox/internal/controller/helper/schedule.go b/controllers/devbox/internal/controller/helper/schedule.go new file mode 100644 index 000000000000..bd86e6c1471e --- /dev/null +++ b/controllers/devbox/internal/controller/helper/schedule.go @@ -0,0 +1,27 @@ +package helper + +const ( + DefaultContainerFSAvailableThreshold = 10 + DefaultCPURequestRatio = 1 + DefaultCPULimitRatio = 2 + DefaultMemoryRequestRatio = 1 + DefaultMemoryLimitRatio = 2 +) + +type AcceptanceConsideration struct { + // The percentage of available bytes required to consider the node suitable for scheduling devbox. + // Default is 10, which means the node must have at least 10% of available bytes in the container filesystem. + ContainerFSAvailableThreshold uint + // The ratio of expected overcommitment (total cpu request / available cpu) of CPU request. + // Default is 1, which means the CPU request cannot be overcommited by more than 100%. + CPURequestRatio uint + // The ratio of expected overcommitment (total cpu limit / available cpu) of CPU limit. + // Default is 2, which means the CPU limit cannot be overcommited by more than 200%. + CPULimitRatio uint + // The ratio of expected overcommitment (total memory request / available memory) of Memory request. + // Default is 1, which means the Memory request cannot be overcommited by more than 100%. + MemoryRequestRatio uint + // The ratio of expected overcommitment (total memory limit / available memory) of Memory limit. + // Default is 2, which means the Memory limit cannot be overcommited by more than 200%. + MemoryLimitRatio uint +} diff --git a/controllers/devbox/internal/stat/stat.go b/controllers/devbox/internal/stat/stat.go new file mode 100644 index 000000000000..c261cf2e9aac --- /dev/null +++ b/controllers/devbox/internal/stat/stat.go @@ -0,0 +1,54 @@ +package stat + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// FsStats contains data about filesystem usage. +// This part of code is taken from k8s.io/kubelet/pkg/apis/stats/v1alpha1 +// Maybe we should import it directly in the future. +type FsStats struct { + // The time at which these stats were updated. + Time metav1.Time `json:"time"` + // AvailableBytes represents the storage space available (bytes) for the filesystem. + // +optional + AvailableBytes *uint64 `json:"availableBytes,omitempty"` + // CapacityBytes represents the total capacity (bytes) of the filesystems underlying storage. + // +optional + CapacityBytes *uint64 `json:"capacityBytes,omitempty"` + // UsedBytes represents the bytes used for a specific task on the filesystem. + // This may differ from the total bytes used on the filesystem and may not equal CapacityBytes - AvailableBytes. + // e.g. For ContainerStats.Rootfs this is the bytes used by the container rootfs on the filesystem. + // +optional + UsedBytes *uint64 `json:"usedBytes,omitempty"` + // InodesFree represents the free inodes in the filesystem. + // +optional + InodesFree *uint64 `json:"inodesFree,omitempty"` + // Inodes represents the total inodes in the filesystem. + // +optional + Inodes *uint64 `json:"inodes,omitempty"` + // InodesUsed represents the inodes used by the filesystem + // This may not equal Inodes - InodesFree because this filesystem may share inodes with other "filesystems" + // e.g. For ContainerStats.Rootfs, this is the inodes used only by that container, and does not count inodes used by other containers. + InodesUsed *uint64 `json:"inodesUsed,omitempty"` +} + +type NodeStatsProvider interface { + ContainerFsStats(ctx context.Context) (FsStats, error) +} +type NodeStatsProviderImpl struct { + // Client *containerd.Client +} + +func (n *NodeStatsProviderImpl) ContainerFsStats(ctx context.Context) (FsStats, error) { + // This is a placeholder for the actual implementation. + // In a real implementation, this would return the filesystem stats of the container. + availableBytes := uint64(1000000000) // Example value + capacityBytes := uint64(2000000000) // Example value + return FsStats{ + AvailableBytes: &availableBytes, // Example value + CapacityBytes: &capacityBytes, // Example value + }, nil +} From 18fa7906d860507391e15cd3a9e0dd3c5e931067 Mon Sep 17 00:00:00 2001 From: yy Date: Tue, 29 Jul 2025 16:29:02 +0800 Subject: [PATCH 05/46] add log for score --- .../internal/controller/devbox_controller.go | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 26685df3d931..4ad6680d0bbd 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -151,19 +151,22 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // if devbox state is running, schedule devbox to node, update devbox status and create a new commit record // and filter out the devbox that are not in the current node if devbox.Spec.State == devboxv1alpha1.DevboxStateRunning { - if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == "" && r.getAcceptanceScore(ctx) >= r.AcceptanceThreshold { - // if devbox is not scheduled to node, schedule it to current node - logger.Info("devbox not scheduled to node, try scheduling to us now", - "nodeName", r.NodeName, - "contentID", devbox.Status.ContentID) - // set up devbox node and content id, new a record for the devbox - devbox.Status.CommitRecords[devbox.Status.ContentID].Node = r.NodeName - if err := r.Status().Update(ctx, devbox); err != nil { - logger.Info("try to schedule devbox to node failed. This devbox may have already been scheduled to another node", "error", err) - return ctrl.Result{}, err + if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == "" { + if score := r.getAcceptanceScore(ctx); score >= r.AcceptanceThreshold { + // if devbox is not scheduled to node, schedule it to current node + logger.Info("devbox not scheduled to node, try scheduling to us now", + "nodeName", r.NodeName, + "devbox", devbox.Name, + "score", score) + // set up devbox node and content id, new a record for the devbox + devbox.Status.CommitRecords[devbox.Status.ContentID].Node = r.NodeName + if err := r.Status().Update(ctx, devbox); err != nil { + logger.Info("try to schedule devbox to node failed. This devbox may have already been scheduled to another node", "error", err) + return ctrl.Result{}, err + } + logger.Info("devbox scheduled to node", "node", r.NodeName) + r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox scheduled to node", "Devbox scheduled to node") } - logger.Info("devbox scheduled to node", "node", r.NodeName) - r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox scheduled to node", "Devbox scheduled to node") } else if devbox.Status.CommitRecords[devbox.Status.ContentID].Node != r.NodeName { logger.Info("devbox already scheduled to node", "node", devbox.Status.CommitRecords[devbox.Status.ContentID].Node) return ctrl.Result{}, nil From c5970aaaed1621942953da5f88b256778b3cad07 Mon Sep 17 00:00:00 2001 From: Yun Pan Date: Wed, 30 Jul 2025 11:05:23 +0800 Subject: [PATCH 06/46] Add NodeStatsProvider to Devbox controller and enhance error handling for container filesystem stats (#42) --- controllers/devbox/cmd/main.go | 2 ++ .../devbox/internal/controller/devbox_controller.go | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index d3a95d2b4a6b..876290a7840f 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -52,6 +52,7 @@ import ( "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/nodes" utilresource "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" + "github.com/labring/sealos/controllers/devbox/internal/stat" // +kubebuilder:scaffold:imports ) @@ -271,6 +272,7 @@ func main() { RestartPredicateDuration: restartPredicateDuration, NodeName: nodes.GetNodeName(), AcceptanceThreshold: acceptanceThreshold, + NodeStatsProvider: &stat.NodeStatsProviderImpl{}, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Devbox") os.Exit(1) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 4ad6680d0bbd..73198771f2aa 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -645,9 +645,15 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { goto unsuitable // If we can't get the acceptance consideration, we assume the node is not suitable } containerFsStats, err = r.ContainerFsStats(ctx) - if err != nil || containerFsStats.AvailableBytes == nil || containerFsStats.CapacityBytes == nil { + if err != nil { logger.Error(err, "failed to get container filesystem stats") goto unsuitable // If we can't get the container filesystem stats, we assume the node is not suitable + } else if containerFsStats.AvailableBytes == nil { + logger.Info("available bytes is nil, assume the node is not suitable") + goto unsuitable // If we can't get the available bytes, we assume the node is not suitable + } else if containerFsStats.CapacityBytes == nil { + logger.Info("capacity bytes is nil, assume the node is not suitable") + goto unsuitable // If we can't get the capacity bytes, we assume the node is not suitable } availableBytes = *containerFsStats.AvailableBytes capacityBytes = *containerFsStats.CapacityBytes From b2a3f324ecc3ee9bfecea5b4bed1a5389f18bcb8 Mon Sep 17 00:00:00 2001 From: yy Date: Wed, 30 Jul 2025 11:06:07 +0800 Subject: [PATCH 07/46] add node rbac --- controllers/devbox/config/rbac/role.yaml | 14 ++++++++++++++ .../internal/controller/devbox_controller.go | 2 ++ 2 files changed, 16 insertions(+) diff --git a/controllers/devbox/config/rbac/role.yaml b/controllers/devbox/config/rbac/role.yaml index 5d76ec1c5344..918201b2f065 100644 --- a/controllers/devbox/config/rbac/role.yaml +++ b/controllers/devbox/config/rbac/role.yaml @@ -13,6 +13,20 @@ rules: - services verbs: - '*' +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - nodes/status + verbs: + - get - apiGroups: - "" resources: diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 73198771f2aa..8b948072300d 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -81,6 +81,8 @@ type DevboxReconciler struct { // +kubebuilder:rbac:groups=devbox.sealos.io,resources=runtimeclasses,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=pods,verbs=* // +kubebuilder:rbac:groups="",resources=pods/status,verbs=get;update;patch +// +kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=nodes/status,verbs=get // +kubebuilder:rbac:groups="",resources=services,verbs=* // +kubebuilder:rbac:groups="",resources=secrets,verbs=* // +kubebuilder:rbac:groups="",resources=events,verbs=* From c59c7d2ee26bbb690633f0361950f4c2d3ad88f2 Mon Sep 17 00:00:00 2001 From: Yun Pan Date: Wed, 30 Jul 2025 15:51:48 +0800 Subject: [PATCH 08/46] Increase some example values and refactor some types (#43) * Increase example values for available and capacity bytes in ContainerFsStats * Refactor resource ratio types to use float64 in acceptance consideration calculations * Change error logging to info level for parsing failures in acceptance consideration --- .../internal/controller/devbox_controller.go | 101 +++++++++--------- .../internal/controller/helper/schedule.go | 30 +++--- controllers/devbox/internal/stat/stat.go | 4 +- 3 files changed, 67 insertions(+), 68 deletions(-) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 8b948072300d..bc4d485d6ff1 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -592,35 +592,35 @@ func (r *DevboxReconciler) getAcceptanceConsideration(ctx context.Context) (help } ann := node.Annotations ac := helper.AcceptanceConsideration{} - if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationContainerFSAvailableThreshold]); err != nil { - logger.Error(err, "failed to parse containerfs available threshold. use default value instead", "value", ann[devboxv1alpha1.AnnotationContainerFSAvailableThreshold]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationContainerFSAvailableThreshold], 64); err != nil { + logger.Info("failed to parse containerfs available threshold. use default value instead", "value", ann[devboxv1alpha1.AnnotationContainerFSAvailableThreshold]) ac.ContainerFSAvailableThreshold = helper.DefaultContainerFSAvailableThreshold } else { - ac.ContainerFSAvailableThreshold = uint(v) + ac.ContainerFSAvailableThreshold = v } - if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationCPURequestRatio]); err != nil { - logger.Error(err, "failed to parse CPU request ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationCPURequestRatio]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationCPURequestRatio], 64); err != nil { + logger.Info("failed to parse CPU request ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationCPURequestRatio]) ac.CPURequestRatio = helper.DefaultCPURequestRatio } else { - ac.CPURequestRatio = uint(v) + ac.CPURequestRatio = v } - if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationCPULimitRatio]); err != nil { - logger.Error(err, "failed to parse CPU limit ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationCPULimitRatio]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationCPULimitRatio], 64); err != nil { + logger.Info("failed to parse CPU limit ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationCPULimitRatio]) ac.CPULimitRatio = helper.DefaultCPULimitRatio } else { - ac.CPULimitRatio = uint(v) + ac.CPULimitRatio = v } - if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationMemoryRequestRatio]); err != nil { - logger.Error(err, "failed to parse memory request ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationMemoryRequestRatio]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationMemoryRequestRatio], 64); err != nil { + logger.Info("failed to parse memory request ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationMemoryRequestRatio]) ac.MemoryRequestRatio = helper.DefaultMemoryRequestRatio } else { - ac.MemoryRequestRatio = uint(v) + ac.MemoryRequestRatio = v } - if v, err := strconv.Atoi(ann[devboxv1alpha1.AnnotationMemoryLimitRatio]); err != nil { - logger.Error(err, "failed to parse memory limit ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationMemoryLimitRatio]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationMemoryLimitRatio], 64); err != nil { + logger.Info("failed to parse memory limit ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationMemoryLimitRatio]) ac.MemoryLimitRatio = helper.DefaultMemoryLimitRatio } else { - ac.MemoryLimitRatio = uint(v) + ac.MemoryLimitRatio = v } return ac, nil } @@ -628,16 +628,16 @@ func (r *DevboxReconciler) getAcceptanceConsideration(ctx context.Context) (help func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { logger := log.FromContext(ctx) var ( - ac helper.AcceptanceConsideration - containerFsStats stat.FsStats - err error - availableBytes uint64 - availablePercentage uint - capacityBytes uint64 - cpuRequestPercentage uint - cpuLimitPercentage uint - memoryRequestPercentage uint - memoryLimitPercentage uint + ac helper.AcceptanceConsideration + containerFsStats stat.FsStats + err error + availableBytes uint64 + availablePercentage float64 + capacityBytes uint64 + cpuRequestRatio float64 + cpuLimitRatio float64 + memoryRequestRatio float64 + memoryLimitRatio float64 score int ) @@ -666,43 +666,43 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { logger.Info("available bytes less than minimum required", "availableBytes", availableBytes, "minimumRequired", minBytesRequired) goto unsuitable // If available bytes are less than the minimum required, we assume the node is not suitable } - availablePercentage = uint(float64(availableBytes) / float64(capacityBytes) * 100) + availablePercentage = float64(availableBytes) / float64(capacityBytes) * 100 if availablePercentage > ac.ContainerFSAvailableThreshold { logger.Info("container filesystem available percentage is greater than threshold", "availablePercentage", availablePercentage, "threshold", ac.ContainerFSAvailableThreshold) score += getScoreUnit(1) } - cpuRequestPercentage, err = r.getTotalCPURequest(ctx, r.NodeName) + cpuRequestRatio, err = r.getTotalCPURequest(ctx, r.NodeName) if err != nil { logger.Error(err, "failed to get total CPU request") goto unsuitable // If we can't get the CPU request, we assume the node is not suitable - } else if cpuRequestPercentage < ac.CPURequestRatio { - logger.Info("cpu request percentage is less than cpu overcommitment request ratio", "requestPercentage", cpuRequestPercentage, "ratio", ac.CPURequestRatio) + } else if cpuRequestRatio < ac.CPURequestRatio { + logger.Info("cpu request ratio is less than cpu overcommitment request ratio", "RequestRatio", cpuRequestRatio, "ratio", ac.CPURequestRatio) score += getScoreUnit(0) } - cpuLimitPercentage, err = r.getTotalCPULimit(ctx, r.NodeName) + cpuLimitRatio, err = r.getTotalCPULimit(ctx, r.NodeName) if err != nil { logger.Error(err, "failed to get total CPU limit") goto unsuitable // If we can't get the CPU limit, we assume the node is not suitable - } else if cpuLimitPercentage < ac.CPULimitRatio { - logger.Info("cpu limit percentage is less than cpu overcommitment limit ratio", "limitPercentage", cpuLimitPercentage, "ratio", ac.CPULimitRatio) + } else if cpuLimitRatio < ac.CPULimitRatio { + logger.Info("cpu limit ratio is less than cpu overcommitment limit ratio", "LimitRatio", cpuLimitRatio, "ratio", ac.CPULimitRatio) score += getScoreUnit(0) } - memoryRequestPercentage, err = r.getTotalMemoryRequest(ctx, r.NodeName) + memoryRequestRatio, err = r.getTotalMemoryRequest(ctx, r.NodeName) if err != nil { logger.Error(err, "failed to get total memory request") goto unsuitable // If we can't get the memory request, we assume the node is not suitable - } else if memoryRequestPercentage < ac.MemoryRequestRatio { - logger.Info("memory request percentage is less than memory overcommitment request ratio", "requestPercentage", memoryRequestPercentage, "ratio", ac.MemoryRequestRatio) + } else if memoryRequestRatio < ac.MemoryRequestRatio { + logger.Info("memory request ratio is less than memory overcommitment request ratio", "RequestRatio", memoryRequestRatio, "ratio", ac.MemoryRequestRatio) score += getScoreUnit(0) } - memoryLimitPercentage, err = r.getTotalMemoryLimit(ctx, r.NodeName) + memoryLimitRatio, err = r.getTotalMemoryLimit(ctx, r.NodeName) if err != nil { logger.Error(err, "failed to get total memory limit") goto unsuitable // If we can't get the memory limit, we assume the node is not suitable - } else if memoryLimitPercentage < ac.MemoryLimitRatio { - logger.Info("memory limit percentage is less than memory overcommitment limit ratio", "limitPercentage", memoryLimitPercentage, "ratio", ac.MemoryLimitRatio) + } else if memoryLimitRatio < ac.MemoryLimitRatio { + logger.Info("memory limit ratio is less than memory overcommitment limit ratio", "LimitRatio", memoryLimitRatio, "ratio", ac.MemoryLimitRatio) score += getScoreUnit(0) } return score @@ -717,7 +717,7 @@ func getScoreUnit(p uint) int { } // getTotalCPURequest returns the total CPU requests (in millicores) for all pods in the namespace. -func (r *DevboxReconciler) getTotalCPURequest(ctx context.Context, namespace string) (uint, error) { +func (r *DevboxReconciler) getTotalCPURequest(ctx context.Context, namespace string) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(namespace), @@ -744,12 +744,11 @@ func (r *DevboxReconciler) getTotalCPURequest(ctx context.Context, namespace str if allocatableMilli == 0 { return 0, fmt.Errorf("node %s allocatable CPU is zero", r.NodeName) } - percentage := uint((float64(totalCPURequest) / float64(allocatableMilli)) * 100) - return percentage, nil + ratio := float64(totalCPURequest) / float64(allocatableMilli) + return ratio, nil } -// getTotalCPULimit returns the total CPU limits (in millicores) for all pods in the namespace. -func (r *DevboxReconciler) getTotalCPULimit(ctx context.Context, namespace string) (uint, error) { +func (r *DevboxReconciler) getTotalCPULimit(ctx context.Context, namespace string) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(namespace), @@ -776,11 +775,11 @@ func (r *DevboxReconciler) getTotalCPULimit(ctx context.Context, namespace strin if allocatableMilli == 0 { return 0, fmt.Errorf("node %s allocatable CPU is zero", r.NodeName) } - percentage := uint((float64(totalCPULimit) / float64(allocatableMilli)) * 100) - return percentage, nil + ratio := float64(totalCPULimit) / float64(allocatableMilli) + return ratio, nil } -func (r *DevboxReconciler) getTotalMemoryRequest(ctx context.Context, namespace string) (uint, error) { +func (r *DevboxReconciler) getTotalMemoryRequest(ctx context.Context, namespace string) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(namespace), @@ -807,11 +806,11 @@ func (r *DevboxReconciler) getTotalMemoryRequest(ctx context.Context, namespace if allocatableBytes == 0 { return 0, fmt.Errorf("node %s allocatable memory is zero", r.NodeName) } - percentage := uint((float64(totalMemoryRequest) / float64(allocatableBytes)) * 100) - return percentage, nil + ratio := float64(totalMemoryRequest) / float64(allocatableBytes) + return ratio, nil } -func (r *DevboxReconciler) getTotalMemoryLimit(ctx context.Context, namespace string) (uint, error) { +func (r *DevboxReconciler) getTotalMemoryLimit(ctx context.Context, namespace string) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(namespace), @@ -838,8 +837,8 @@ func (r *DevboxReconciler) getTotalMemoryLimit(ctx context.Context, namespace st if allocatableBytes == 0 { return 0, fmt.Errorf("node %s allocatable memory is zero", r.NodeName) } - percentage := uint((float64(totalMemoryLimit) / float64(allocatableBytes)) * 100) - return percentage, nil + ratio := float64(totalMemoryLimit) / float64(allocatableBytes) + return ratio, nil } type ControllerRestartPredicate struct { diff --git a/controllers/devbox/internal/controller/helper/schedule.go b/controllers/devbox/internal/controller/helper/schedule.go index bd86e6c1471e..46ae9ac49553 100644 --- a/controllers/devbox/internal/controller/helper/schedule.go +++ b/controllers/devbox/internal/controller/helper/schedule.go @@ -1,27 +1,27 @@ package helper const ( - DefaultContainerFSAvailableThreshold = 10 - DefaultCPURequestRatio = 1 - DefaultCPULimitRatio = 2 - DefaultMemoryRequestRatio = 1 - DefaultMemoryLimitRatio = 2 + DefaultContainerFSAvailableThreshold = 10.0 + DefaultCPURequestRatio = 1.0 + DefaultCPULimitRatio = 2.0 + DefaultMemoryRequestRatio = 1.0 + DefaultMemoryLimitRatio = 2.0 ) type AcceptanceConsideration struct { // The percentage of available bytes required to consider the node suitable for scheduling devbox. - // Default is 10, which means the node must have at least 10% of available bytes in the container filesystem. - ContainerFSAvailableThreshold uint + // Default is 10.0, which means the node must have at least 10% of available bytes in the container filesystem. + ContainerFSAvailableThreshold float64 // The ratio of expected overcommitment (total cpu request / available cpu) of CPU request. - // Default is 1, which means the CPU request cannot be overcommited by more than 100%. - CPURequestRatio uint + // Default is 1.0, which means the CPU request cannot be overcommited by more than 100%. + CPURequestRatio float64 // The ratio of expected overcommitment (total cpu limit / available cpu) of CPU limit. - // Default is 2, which means the CPU limit cannot be overcommited by more than 200%. - CPULimitRatio uint + // Default is 2.0, which means the CPU limit cannot be overcommited by more than 200%. + CPULimitRatio float64 // The ratio of expected overcommitment (total memory request / available memory) of Memory request. - // Default is 1, which means the Memory request cannot be overcommited by more than 100%. - MemoryRequestRatio uint + // Default is 1.0, which means the Memory request cannot be overcommited by more than 100%. + MemoryRequestRatio float64 // The ratio of expected overcommitment (total memory limit / available memory) of Memory limit. - // Default is 2, which means the Memory limit cannot be overcommited by more than 200%. - MemoryLimitRatio uint + // Default is 2.0, which means the Memory limit cannot be overcommited by more than 200%. + MemoryLimitRatio float64 } diff --git a/controllers/devbox/internal/stat/stat.go b/controllers/devbox/internal/stat/stat.go index c261cf2e9aac..2f6b21d5d9d6 100644 --- a/controllers/devbox/internal/stat/stat.go +++ b/controllers/devbox/internal/stat/stat.go @@ -45,8 +45,8 @@ type NodeStatsProviderImpl struct { func (n *NodeStatsProviderImpl) ContainerFsStats(ctx context.Context) (FsStats, error) { // This is a placeholder for the actual implementation. // In a real implementation, this would return the filesystem stats of the container. - availableBytes := uint64(1000000000) // Example value - capacityBytes := uint64(2000000000) // Example value + availableBytes := uint64(10000000000) // Example value + capacityBytes := uint64(20000000000) // Example value return FsStats{ AvailableBytes: &availableBytes, // Example value CapacityBytes: &capacityBytes, // Example value From c4c43365baf4d25d02bbb1639befa21813a61e93 Mon Sep 17 00:00:00 2001 From: yy Date: Wed, 30 Jul 2025 16:12:52 +0800 Subject: [PATCH 09/46] fix --- .../devbox/internal/controller/devbox_controller.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index bc4d485d6ff1..cfbca6bcb4fc 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -159,7 +159,8 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr logger.Info("devbox not scheduled to node, try scheduling to us now", "nodeName", r.NodeName, "devbox", devbox.Name, - "score", score) + "score", score, + "acceptanceThreshold", r.AcceptanceThreshold) // set up devbox node and content id, new a record for the devbox devbox.Status.CommitRecords[devbox.Status.ContentID].Node = r.NodeName if err := r.Status().Update(ctx, devbox); err != nil { @@ -168,6 +169,13 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } logger.Info("devbox scheduled to node", "node", r.NodeName) r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox scheduled to node", "Devbox scheduled to node") + } else { + logger.Info("devbox not scheduled to node, try scheduling to us later", + "nodeName", r.NodeName, + "devbox", devbox.Name, + "score", score, + "acceptanceThreshold", r.AcceptanceThreshold) + return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } } else if devbox.Status.CommitRecords[devbox.Status.ContentID].Node != r.NodeName { logger.Info("devbox already scheduled to node", "node", devbox.Status.CommitRecords[devbox.Status.ContentID].Node) @@ -187,9 +195,6 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // create service if network type is NodePort if devbox.Spec.NetworkSpec.Type == devboxv1alpha1.NetworkTypeNodePort { logger.Info("syncing service") - if err := r.Get(ctx, req.NamespacedName, devbox); err != nil { - return ctrl.Result{}, err - } if err := r.syncService(ctx, devbox, recLabels); err != nil { logger.Error(err, "sync service failed") r.Recorder.Eventf(devbox, corev1.EventTypeWarning, "Sync service failed", "%v", err) From 86c5ddca0255f8742a65be100af43759a762ac45 Mon Sep 17 00:00:00 2001 From: Yun Pan Date: Thu, 31 Jul 2025 10:16:12 +0800 Subject: [PATCH 10/46] Minor fixes for acceptance scoring (#44) * Modify acceptance score calculation by using storage limit consideration instead and refactor related methods * Rename ratio functions and add up cpu by Value() instead of MilliValue() * Increase example values for available and capacity bytes to 100G/200G --- .../internal/controller/devbox_controller.go | 46 ++++++++++--------- .../internal/controller/helper/devbox.go | 11 +++++ controllers/devbox/internal/stat/stat.go | 4 +- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index cfbca6bcb4fc..741d9238449e 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -154,7 +154,7 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // and filter out the devbox that are not in the current node if devbox.Spec.State == devboxv1alpha1.DevboxStateRunning { if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == "" { - if score := r.getAcceptanceScore(ctx); score >= r.AcceptanceThreshold { + if score := r.getAcceptanceScore(ctx, devbox); score >= r.AcceptanceThreshold { // if devbox is not scheduled to node, schedule it to current node logger.Info("devbox not scheduled to node, try scheduling to us now", "nodeName", r.NodeName, @@ -630,7 +630,7 @@ func (r *DevboxReconciler) getAcceptanceConsideration(ctx context.Context) (help return ac, nil } -func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { +func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context, devbox *devboxv1alpha1.Devbox) int { logger := log.FromContext(ctx) var ( ac helper.AcceptanceConsideration @@ -643,6 +643,7 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { cpuLimitRatio float64 memoryRequestRatio float64 memoryLimitRatio float64 + storageLimitBytes int64 score int ) @@ -664,12 +665,12 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { } availableBytes = *containerFsStats.AvailableBytes capacityBytes = *containerFsStats.CapacityBytes - if minBytesRequired, ok := r.EphemeralStorage.MaximumLimit.AsInt64(); !ok { - logger.Error(err, "failed to get minimum bytes required for ephemeral storage") - goto unsuitable // If we can't get the minimum bytes required, we assume the node is not suitable - } else if availableBytes < uint64(minBytesRequired) { - logger.Info("available bytes less than minimum required", "availableBytes", availableBytes, "minimumRequired", minBytesRequired) - goto unsuitable // If available bytes are less than the minimum required, we assume the node is not suitable + if storageLimitBytes, err = helper.GetStorageLimitInBytes(devbox); err != nil { + logger.Error(err, "failed to get storage limit") + goto unsuitable // If we can't get the storage limit, we assume the node is not suitable + } else if availableBytes < uint64(storageLimitBytes) { + logger.Info("available bytes less than storage limit", "availableBytes", availableBytes, "storageLimitBytes", storageLimitBytes) + goto unsuitable // If available bytes are less than the storage limit, we assume the node is not suitable } availablePercentage = float64(availableBytes) / float64(capacityBytes) * 100 if availablePercentage > ac.ContainerFSAvailableThreshold { @@ -678,7 +679,7 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { "threshold", ac.ContainerFSAvailableThreshold) score += getScoreUnit(1) } - cpuRequestRatio, err = r.getTotalCPURequest(ctx, r.NodeName) + cpuRequestRatio, err = r.getTotalCPURequestRatio(ctx, r.NodeName) if err != nil { logger.Error(err, "failed to get total CPU request") goto unsuitable // If we can't get the CPU request, we assume the node is not suitable @@ -686,7 +687,7 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { logger.Info("cpu request ratio is less than cpu overcommitment request ratio", "RequestRatio", cpuRequestRatio, "ratio", ac.CPURequestRatio) score += getScoreUnit(0) } - cpuLimitRatio, err = r.getTotalCPULimit(ctx, r.NodeName) + cpuLimitRatio, err = r.getTotalCPULimitRatio(ctx, r.NodeName) if err != nil { logger.Error(err, "failed to get total CPU limit") goto unsuitable // If we can't get the CPU limit, we assume the node is not suitable @@ -694,7 +695,7 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { logger.Info("cpu limit ratio is less than cpu overcommitment limit ratio", "LimitRatio", cpuLimitRatio, "ratio", ac.CPULimitRatio) score += getScoreUnit(0) } - memoryRequestRatio, err = r.getTotalMemoryRequest(ctx, r.NodeName) + memoryRequestRatio, err = r.getTotalMemoryRequestRatio(ctx, r.NodeName) if err != nil { logger.Error(err, "failed to get total memory request") goto unsuitable // If we can't get the memory request, we assume the node is not suitable @@ -702,7 +703,7 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context) int { logger.Info("memory request ratio is less than memory overcommitment request ratio", "RequestRatio", memoryRequestRatio, "ratio", ac.MemoryRequestRatio) score += getScoreUnit(0) } - memoryLimitRatio, err = r.getTotalMemoryLimit(ctx, r.NodeName) + memoryLimitRatio, err = r.getTotalMemoryLimitRatio(ctx, r.NodeName) if err != nil { logger.Error(err, "failed to get total memory limit") goto unsuitable // If we can't get the memory limit, we assume the node is not suitable @@ -721,8 +722,8 @@ func getScoreUnit(p uint) int { return 16 << (p * 4) } -// getTotalCPURequest returns the total CPU requests (in millicores) for all pods in the namespace. -func (r *DevboxReconciler) getTotalCPURequest(ctx context.Context, namespace string) (float64, error) { +// getTotalCPURequestRatio returns the total CPU requests (in millicores) ratio for all pods in the namespace. +func (r *DevboxReconciler) getTotalCPURequestRatio(ctx context.Context, namespace string) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(namespace), @@ -736,7 +737,7 @@ func (r *DevboxReconciler) getTotalCPURequest(ctx context.Context, namespace str for _, container := range pod.Spec.Containers { if cpuReq, ok := container.Resources.Requests[corev1.ResourceCPU]; ok { // TODO: check if this could lead to overflow - totalCPURequest += cpuReq.MilliValue() + totalCPURequest += cpuReq.Value() } } } @@ -745,7 +746,7 @@ func (r *DevboxReconciler) getTotalCPURequest(ctx context.Context, namespace str return 0, err } allocatableCPU := node.Status.Allocatable[corev1.ResourceCPU] - allocatableMilli := allocatableCPU.MilliValue() + allocatableMilli := allocatableCPU.Value() if allocatableMilli == 0 { return 0, fmt.Errorf("node %s allocatable CPU is zero", r.NodeName) } @@ -753,7 +754,8 @@ func (r *DevboxReconciler) getTotalCPURequest(ctx context.Context, namespace str return ratio, nil } -func (r *DevboxReconciler) getTotalCPULimit(ctx context.Context, namespace string) (float64, error) { +// getTotalCPULimitRatio returns the total CPU limits (in millicores) ratio for all pods in the namespace. +func (r *DevboxReconciler) getTotalCPULimitRatio(ctx context.Context, namespace string) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(namespace), @@ -767,7 +769,7 @@ func (r *DevboxReconciler) getTotalCPULimit(ctx context.Context, namespace strin for _, container := range pod.Spec.Containers { if cpuLimit, ok := container.Resources.Limits[corev1.ResourceCPU]; ok { // TODO: check if this could lead to overflow - totalCPULimit += cpuLimit.MilliValue() + totalCPULimit += cpuLimit.Value() } } } @@ -776,7 +778,7 @@ func (r *DevboxReconciler) getTotalCPULimit(ctx context.Context, namespace strin return 0, err } allocatableCPU := node.Status.Allocatable[corev1.ResourceCPU] - allocatableMilli := allocatableCPU.MilliValue() + allocatableMilli := allocatableCPU.Value() if allocatableMilli == 0 { return 0, fmt.Errorf("node %s allocatable CPU is zero", r.NodeName) } @@ -784,7 +786,8 @@ func (r *DevboxReconciler) getTotalCPULimit(ctx context.Context, namespace strin return ratio, nil } -func (r *DevboxReconciler) getTotalMemoryRequest(ctx context.Context, namespace string) (float64, error) { +// getTotalMemoryRequestRatio returns the total memory requests ratio for all pods in the namespace. +func (r *DevboxReconciler) getTotalMemoryRequestRatio(ctx context.Context, namespace string) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(namespace), @@ -815,7 +818,8 @@ func (r *DevboxReconciler) getTotalMemoryRequest(ctx context.Context, namespace return ratio, nil } -func (r *DevboxReconciler) getTotalMemoryLimit(ctx context.Context, namespace string) (float64, error) { +// getTotalMemoryLimitRatio returns the total memory limits ratio for all pods in the namespace. +func (r *DevboxReconciler) getTotalMemoryLimitRatio(ctx context.Context, namespace string) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(namespace), diff --git a/controllers/devbox/internal/controller/helper/devbox.go b/controllers/devbox/internal/controller/helper/devbox.go index fd214c2a00f1..88880457acdc 100644 --- a/controllers/devbox/internal/controller/helper/devbox.go +++ b/controllers/devbox/internal/controller/helper/devbox.go @@ -268,3 +268,14 @@ func GetArgs(devbox *devboxv1alpha1.Devbox) []string { func IsExceededQuotaError(err error) bool { return strings.Contains(err.Error(), "exceeded quota") } + +func GetStorageLimitInBytes(devbox *devboxv1alpha1.Devbox) (int64, error) { + if devbox.Spec.StorageLimit != "" { + storageLimit, err := resource.ParseQuantity(devbox.Spec.StorageLimit) + if err != nil { + return 0, err + } + return storageLimit.Value(), nil + } + return 0, nil +} diff --git a/controllers/devbox/internal/stat/stat.go b/controllers/devbox/internal/stat/stat.go index 2f6b21d5d9d6..cc4258226c57 100644 --- a/controllers/devbox/internal/stat/stat.go +++ b/controllers/devbox/internal/stat/stat.go @@ -45,8 +45,8 @@ type NodeStatsProviderImpl struct { func (n *NodeStatsProviderImpl) ContainerFsStats(ctx context.Context) (FsStats, error) { // This is a placeholder for the actual implementation. // In a real implementation, this would return the filesystem stats of the container. - availableBytes := uint64(10000000000) // Example value - capacityBytes := uint64(20000000000) // Example value + availableBytes := uint64(100000000000) // Example value + capacityBytes := uint64(200000000000) // Example value return FsStats{ AvailableBytes: &availableBytes, // Example value CapacityBytes: &capacityBytes, // Example value From 220bc00377ab28caa62862e42a5baa5a92e9ef88 Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Fri, 1 Aug 2025 10:20:53 +0800 Subject: [PATCH 11/46] fix snapshot error Co-authored-by: Cunzili --- controllers/devbox/go.mod | 8 +- controllers/devbox/go.sum | 12 +- controllers/devbox/internal/commit/commit.go | 181 ++++++++++++++---- .../devbox/internal/commit/commit_test.go | 88 ++++++++- controllers/devbox/internal/commit/const.go | 9 +- controllers/go.work.sum | 6 + 6 files changed, 248 insertions(+), 56 deletions(-) diff --git a/controllers/devbox/go.mod b/controllers/devbox/go.mod index 3b30825840f8..cacc6cd80a72 100644 --- a/controllers/devbox/go.mod +++ b/controllers/devbox/go.mod @@ -20,11 +20,11 @@ require ( sigs.k8s.io/controller-runtime v0.20.4 ) -replace github.com/containerd/nerdctl => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 +replace github.com/containerd/nerdctl => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27 -replace github.com/containerd/nerdctl/v2 => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 +replace github.com/containerd/nerdctl/v2 => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27 -replace github.com/containerd/nerdctl/mod/tigron => github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2 +replace github.com/containerd/nerdctl/mod/tigron => github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250731025443-879dbf5c7d27 require ( cel.dev/expr v0.20.0 // indirect @@ -87,7 +87,7 @@ require ( github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/go-viper/mapstructure/v2 v2.3.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/controllers/devbox/go.sum b/controllers/devbox/go.sum index acef4f142598..15cf98a5b919 100644 --- a/controllers/devbox/go.sum +++ b/controllers/devbox/go.sum @@ -150,8 +150,8 @@ github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7 github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= -github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -232,10 +232,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2 h1:ZZSdAQJUwb+HyWle8w+B+vLZodSJaeKMojEOdWXYuUU= -github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2/go.mod h1:gmUZh2wUVxr/msGogKUi6v9eJbP5ASO4fVYEPzHH4iI= -github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 h1:Xd/Ucu301cld+j+E/+nf0aeQ5HOwmZfJ+WH2dCjCy3Y= -github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2/go.mod h1:un/Eqg9DrVwazXHukMsxq+PEBixrN7YC3NRXdx5J3tY= +github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250731025443-879dbf5c7d27 h1:9fqCrTFVxfoTlRwzJaOemDZBEEQdLHrqbIfOWhjGm1Y= +github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250731025443-879dbf5c7d27/go.mod h1:gmUZh2wUVxr/msGogKUi6v9eJbP5ASO4fVYEPzHH4iI= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27 h1:SkN036GKQ+DOJHVmD3iz2RxggOkxy+suE4ySluf+79Q= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27/go.mod h1:3IzoMVp0HIP/lMWRA4xzTMghuKJ4F3fFWTtGAfrFhzk= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 51e9c63b67e9..7a71b58663cf 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -3,13 +3,17 @@ package commit import ( "context" "fmt" - "log" "io" + "log" + "strings" + "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/namespaces" - "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/nerdctl/v2/pkg/api/types" "github.com/containerd/nerdctl/v2/pkg/cmd/container" + "github.com/containerd/nerdctl/v2/pkg/containerutil" + ncdefaults "github.com/containerd/nerdctl/v2/pkg/defaults" + "github.com/labring/sealos/controllers/devbox/api/v1alpha1" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" @@ -47,43 +51,70 @@ func NewCommitter() (Committer, error) { }, nil } -// CreateContainer create container +// CreateContainer create container with labels func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, contentID string, baseImage string) (string, error) { fmt.Println("========>>>> create container", devboxName, contentID, baseImage) - // 1. get image ctx = namespaces.WithNamespace(ctx, DefaultNamespace) - image, err := c.containerdClient.GetImage(ctx, baseImage) - if err != nil { - // image not found, try to pull - log.Printf("Image %s not found, pulling...", baseImage) - image, err = c.containerdClient.Pull(ctx, baseImage, client.WithPullUnpack) - if err != nil { - return "", fmt.Errorf("failed to pull image %s: %v", baseImage, err) - } + global := NewGlobalOptionConfig() + + // create container with labels + originalAnnotations := map[string]string{ + v1alpha1.AnnotationContentID: contentID, + v1alpha1.AnnotationInit: AnnotationImageFromValue, + v1alpha1.AnnotationStorageLimit: AnnotationUseLimitValue, + AnnotationKeyNamespace: DefaultNamespace, + AnnotationKeyImageName: baseImage, } - // 2. create container - // add annotations/labels - annotations := map[string]string{ - AnnotationKeyContentID: contentID, - AnnotationKeyNamespace: DefaultNamespace, - AnnotationKeyImageName: baseImage, + // convert labels to "containerd.io/snapshot/devbox-" format + convertedLabels := convertLabels(originalAnnotations) + convertedAnnotations := convertMapToSlice(originalAnnotations) + + // create container options + createOpt := types.ContainerCreateOptions{ + GOptions: *global, + Runtime: DefaultRuntime, // user devbox runtime + Name: fmt.Sprintf("devbox-%s-container", devboxName), + Pull: "missing", + InRun: false, // not start container + Rm: false, + LogDriver: "json-file", + StopSignal: "SIGTERM", + Restart: "unless-stopped", + Interactive: false, // not interactive, avoid conflict with Detach + Cgroupns: "host", // add cgroupns mode + Detach: true, // run in background + Rootfs: false, + Label: convertedAnnotations, + SnapshotLabels: convertedLabels, + ImagePullOpt: types.ImagePullOptions{ + GOptions: types.GlobalCommandOptions{ + Snapshotter: DefaultSnapshotter, + }, + }, } - containerName := fmt.Sprintf("%s-container", devboxName) // container name - - container, err := c.containerdClient.NewContainer(ctx, containerName, - client.WithImage(image), - client.WithNewSnapshot(containerName, image), - client.WithContainerLabels(annotations), // add annotations - client.WithNewSpec(oci.WithImageConfig(image)), // oci.WithProcessArgs("/bin/sh", "-c", "while true; do echo 'Hello, World!'; sleep 5; done"), - // oci.WithHostname("test-container"), + // create network manager + networkManager, err := containerutil.NewNetworkingOptionsManager(createOpt.GOptions, + types.NetworkOptions{ + NetworkSlice: []string{DefaultNetworkMode}, + }, c.containerdClient) + if err != nil { + log.Println("failed to create network manager:", err) + return "", fmt.Errorf("failed to create network manager: %v", err) + } - ) + // create container + container, cleanup, err := container.Create(ctx, c.containerdClient, []string{originalAnnotations[AnnotationKeyImageName]}, networkManager, createOpt) if err != nil { + log.Println("failed to create container:", err) return "", fmt.Errorf("failed to create container: %v", err) } + if cleanup != nil { + defer cleanup() + } + log.Printf("container created successfully: %s\n", container.ID()) return container.ID(), nil } @@ -125,7 +156,24 @@ func (c *CommitterImpl) DeleteContainer(ctx context.Context, containerName strin return fmt.Errorf("failed to delete container: %v", err) } - log.Printf("Container deleted: %s", containerName) + log.Printf("Container deleted: %s successfully", containerName) + return nil +} + +// RemoveContainer remove container +func (c *CommitterImpl) RemoveContainer(ctx context.Context, containerNames string) error { + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + global := NewGlobalOptionConfig() + opt := types.ContainerRemoveOptions{ + Stdout: io.Discard, + Force: false, + Volumes: false, + GOptions: *global, + } + err := container.Remove(ctx, c.containerdClient, []string{containerNames}, opt) + if err != nil { + return fmt.Errorf("failed to remove container: %v", err) + } return nil } @@ -138,22 +186,31 @@ func (c *CommitterImpl) Commit(ctx context.Context, devboxName string, contentID return fmt.Errorf("failed to create container: %v", err) } - global := types.GlobalCommandOptions{ - Namespace: DefaultNamespace, - Address: DefaultContainerdAddress, - DataRoot: DefaultDataRoot, - InsecureRegistry: InsecureRegistry, - } - + // create commit options + global := NewGlobalOptionConfig() opt := types.ContainerCommitOptions{ Stdout: io.Discard, - GOptions: global, + GOptions: *global, Pause: PauseContainerDuringCommit, DevboxOptions: types.DevboxOptions{ RemoveBaseImageTopLayer: DevboxOptionsRemoveBaseImageTopLayer, }, } - return container.Commit(ctx, c.containerdClient, commitImage, containerID, opt) + + // commit container + err = container.Commit(ctx, c.containerdClient, commitImage, containerID, opt) + // if commit failed, delete container + if err != nil { + // delete container + err = c.RemoveContainer(ctx, containerID) + if err != nil { + log.Printf("Warning: failed to delete container %s: %v", containerID, err) + } + return fmt.Errorf("failed to commit container: %v", err) + } + + // commit success, delete container + return c.RemoveContainer(ctx, containerID) } // GetContainerAnnotations get container annotations @@ -206,7 +263,7 @@ func (h *Handler) GC(ctx context.Context) error { continue } // if container is not devbox container, skip - if _, ok := labels[AnnotationKeyContentID]; !ok { + if _, ok := labels[v1alpha1.AnnotationContentID]; !ok { continue } @@ -246,8 +303,54 @@ func (h *Handler) GC(ctx context.Context) error { deletedContainersCount++ } } - } log.Printf("GC completed, deleted %d containers", deletedContainersCount) return nil } + +// convertLabels convert labels to "containerd.io/snapshot/devbox-" format +func convertLabels(labels map[string]string) map[string]string { + convertedLabels := make(map[string]string) + for key, value := range labels { + if strings.HasPrefix(key, ContainerLabelPrefix) { + // convert "devbox.sealos.io/" to "containerd.io/snapshot/devbox-" + newKey := SnapshotLabelPrefix + key[len(ContainerLabelPrefix):] + convertedLabels[newKey] = value + } + } + return convertedLabels +} + +// convertMapToSlice convert map to slice +func convertMapToSlice(labels map[string]string) []string { + slice := make([]string, 0, len(labels)) + for key, value := range labels { + slice = append(slice, fmt.Sprintf("%s=%s", key, value)) + } + return slice +} + +// NewGlobalOptionConfig new global option config +func NewGlobalOptionConfig() *types.GlobalCommandOptions { + return &types.GlobalCommandOptions{ + Namespace: DefaultNamespace, + Address: DefaultContainerdAddress, + DataRoot: DefaultDataRoot, + Debug: false, + DebugFull: false, + Snapshotter: DefaultSnapshotter, + CNIPath: ncdefaults.CNIPath(), + CNINetConfPath: ncdefaults.CNINetConfPath(), + CgroupManager: ncdefaults.CgroupManager(), + InsecureRegistry: false, + HostsDir: ncdefaults.HostsDirs(), + Experimental: true, + HostGatewayIP: ncdefaults.HostGatewayIP(), + KubeHideDupe: false, + CDISpecDirs: ncdefaults.CDISpecDirs(), + UsernsRemap: "", + DNS: []string{}, + DNSOpts: []string{}, + DNSSearch: []string{}, + } +} diff --git a/controllers/devbox/internal/commit/commit_test.go b/controllers/devbox/internal/commit/commit_test.go index 47c8a143f271..378011dd5688 100644 --- a/controllers/devbox/internal/commit/commit_test.go +++ b/controllers/devbox/internal/commit/commit_test.go @@ -6,9 +6,10 @@ import ( "sync" "testing" "time" + + "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/containerd/containerd/v2/pkg/namespaces" ) // init Committer @@ -45,17 +46,16 @@ func TestCreateContainer(t *testing.T) { // create container devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) - contentID := "test-content-id-456" + contentID := fmt.Sprintf("test-content-id-%d", time.Now().Unix()) baseImage := "docker.io/library/nginx:latest" // use another public image to test - containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) assert.NoError(t, err) assert.NotEmpty(t, containerID) // verify container labels annotations, err := committer.(*CommitterImpl).GetContainerAnnotations(ctx, containerID) + fmt.Printf("annotations: %+v\n", annotations) assert.NoError(t, err) - assert.Equal(t, contentID, annotations["devbox.sealos.io/content-id"]) } // test delete container @@ -98,6 +98,45 @@ func TestDeleteContainer(t *testing.T) { assert.Error(t, err) } +// test remove container +func TestRemoveContainer(t *testing.T) { + ctx := context.Background() + committer, err := NewCommitter() + assert.NoError(t, err) + // create a container + devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, "test-content-id-789", "docker.io/library/alpine:latest") + assert.NoError(t, err) + + // show all containers in current namespace + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + containers, err := committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + + fmt.Printf("=== All Containers in current namespace ===\n") + for _, container := range containers { + fmt.Printf("Container ID: %s\n", container.ID()) + } + fmt.Printf("=== Total %d containers ===\n", len(containers)) + + // delete container + err = committer.(*CommitterImpl).RemoveContainer(ctx, containerID) + assert.NoError(t, err) + + containers, err = committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + + fmt.Printf("=== All Containers in current namespace ===\n") + for _, container := range containers { + fmt.Printf("Container ID: %s\n", container.ID()) + } + fmt.Printf("=== Total %d containers ===\n", len(containers)) + + // verify container is deleted (try to get labels should return error) + _, err = committer.(*CommitterImpl).GetContainerAnnotations(ctx, containerID) + assert.Error(t, err) +} + // test error cases func TestErrorCases(t *testing.T) { ctx := context.Background() @@ -150,7 +189,10 @@ func TestConcurrentOperations(t *testing.T) { // delete containers for _, containerID := range containers { - committer.(*CommitterImpl).DeleteContainer(ctx, containerID) + err := committer.(*CommitterImpl).RemoveContainer(ctx, containerID) + if err != nil { + t.Logf("Warning: failed to delete container %s: %v", containerID, err) + } } // get current containers list @@ -161,7 +203,6 @@ func TestConcurrentOperations(t *testing.T) { fmt.Printf("Container ID: %s\n", container.ID()) } fmt.Printf("=== Total %d containers ===\n", len(currentContainers)) - } // test gc handler @@ -212,6 +253,41 @@ func TestGCHandler(t *testing.T) { fmt.Printf("=== Total %d containers ===\n", len(afterContainers)) } +// test runtime selection +func TestRuntimeSelection(t *testing.T) { + ctx := context.Background() + committer, err := NewCommitter() + assert.NoError(t, err) + + // create container with specific runtime + devboxName := fmt.Sprintf("test-runtime-%d", time.Now().Unix()) + contentID := "test-runtime-content-id" + baseImage := "docker.io/library/busybox:latest" + + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) + assert.NoError(t, err) + assert.NotEmpty(t, containerID) + + // get container info to verify runtime + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + container, err := committer.(*CommitterImpl).containerdClient.LoadContainer(ctx, containerID) + assert.NoError(t, err) + + info, err := container.Info(ctx) + assert.NoError(t, err) + + fmt.Printf("=== Container Runtime Information ===\n") + fmt.Printf("Container ID: %s\n", containerID) + fmt.Printf("Runtime Name: %s\n", info.Runtime.Name) + fmt.Printf("Runtime Options: %+v\n", info.Runtime.Options) + fmt.Printf("Expected Runtime: %s\n", DefaultRuntime) + fmt.Printf("Runtime Match: %v\n", info.Runtime.Name == DefaultRuntime) + + // cleanup + err = committer.(*CommitterImpl).DeleteContainer(ctx, containerID) + assert.NoError(t, err) +} + // // test large image // func TestLargeImage(t *testing.T) { // ctx := context.Background() diff --git a/controllers/devbox/internal/commit/const.go b/controllers/devbox/internal/commit/const.go index 923f2495a002..46dc8d3f51ae 100644 --- a/controllers/devbox/internal/commit/const.go +++ b/controllers/devbox/internal/commit/const.go @@ -4,11 +4,18 @@ const ( DefaultNamespace = "sealos.io" DefaultContainerdAddress = "unix:///var/run/containerd/containerd.sock" DefaultDataRoot = "/var/lib/containerd" + DefaultRuntime = "devbox-runc" + DefaultSnapshotter = "devbox" + DefaultNetworkMode = "none" InsecureRegistry = true PauseContainerDuringCommit = false - AnnotationKeyContentID = "devbox.sealos.io/content-id" AnnotationKeyNamespace = "namespace" AnnotationKeyImageName = "image.name" DevboxOptionsRemoveBaseImageTopLayer = true + AnnotationImageFromValue = "true" + AnnotationUseLimitValue = "1Gi" + + SnapshotLabelPrefix = "containerd.io/snapshot/devbox-" + ContainerLabelPrefix = "devbox.sealos.io/" ) diff --git a/controllers/go.work.sum b/controllers/go.work.sum index 1f7800b2a4bc..3a0c46cbfb59 100644 --- a/controllers/go.work.sum +++ b/controllers/go.work.sum @@ -2864,6 +2864,12 @@ github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e/go.mod h1:9z/y9glKmWEdV50tjlUPxFwi1goQfIrrsoZbnMyIZbY= +github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2 h1:ZZSdAQJUwb+HyWle8w+B+vLZodSJaeKMojEOdWXYuUU= +github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2/go.mod h1:gmUZh2wUVxr/msGogKUi6v9eJbP5ASO4fVYEPzHH4iI= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 h1:Xd/Ucu301cld+j+E/+nf0aeQ5HOwmZfJ+WH2dCjCy3Y= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2/go.mod h1:un/Eqg9DrVwazXHukMsxq+PEBixrN7YC3NRXdx5J3tY= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27 h1:SkN036GKQ+DOJHVmD3iz2RxggOkxy+suE4ySluf+79Q= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27/go.mod h1:3IzoMVp0HIP/lMWRA4xzTMghuKJ4F3fFWTtGAfrFhzk= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= From bd176bc40495155b3400c7e5ad2cd846e33f245c Mon Sep 17 00:00:00 2001 From: Yun Pan Date: Fri, 1 Aug 2025 11:34:47 +0800 Subject: [PATCH 12/46] Add PodNodeNameIndex constant and refactor CPU/Memory ratio functions to use it (#46) --- .../devbox/api/v1alpha1/devbox_types.go | 2 + .../internal/controller/devbox_controller.go | 37 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/controllers/devbox/api/v1alpha1/devbox_types.go b/controllers/devbox/api/v1alpha1/devbox_types.go index 031eed27b18e..08097da7662d 100644 --- a/controllers/devbox/api/v1alpha1/devbox_types.go +++ b/controllers/devbox/api/v1alpha1/devbox_types.go @@ -42,6 +42,8 @@ const ( // Label the devbox pod with the devbox part of LabelDevBoxPartOf = "devbox" + // Index for pod node name + PodNodeNameIndex = "spec.nodeName" ) type DevboxState string diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 741d9238449e..74bf58eafb8e 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -679,7 +679,7 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context, devbox *devbo "threshold", ac.ContainerFSAvailableThreshold) score += getScoreUnit(1) } - cpuRequestRatio, err = r.getTotalCPURequestRatio(ctx, r.NodeName) + cpuRequestRatio, err = r.getTotalCPURequestRatio(ctx) if err != nil { logger.Error(err, "failed to get total CPU request") goto unsuitable // If we can't get the CPU request, we assume the node is not suitable @@ -687,7 +687,7 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context, devbox *devbo logger.Info("cpu request ratio is less than cpu overcommitment request ratio", "RequestRatio", cpuRequestRatio, "ratio", ac.CPURequestRatio) score += getScoreUnit(0) } - cpuLimitRatio, err = r.getTotalCPULimitRatio(ctx, r.NodeName) + cpuLimitRatio, err = r.getTotalCPULimitRatio(ctx) if err != nil { logger.Error(err, "failed to get total CPU limit") goto unsuitable // If we can't get the CPU limit, we assume the node is not suitable @@ -695,7 +695,7 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context, devbox *devbo logger.Info("cpu limit ratio is less than cpu overcommitment limit ratio", "LimitRatio", cpuLimitRatio, "ratio", ac.CPULimitRatio) score += getScoreUnit(0) } - memoryRequestRatio, err = r.getTotalMemoryRequestRatio(ctx, r.NodeName) + memoryRequestRatio, err = r.getTotalMemoryRequestRatio(ctx) if err != nil { logger.Error(err, "failed to get total memory request") goto unsuitable // If we can't get the memory request, we assume the node is not suitable @@ -703,7 +703,7 @@ func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context, devbox *devbo logger.Info("memory request ratio is less than memory overcommitment request ratio", "RequestRatio", memoryRequestRatio, "ratio", ac.MemoryRequestRatio) score += getScoreUnit(0) } - memoryLimitRatio, err = r.getTotalMemoryLimitRatio(ctx, r.NodeName) + memoryLimitRatio, err = r.getTotalMemoryLimitRatio(ctx) if err != nil { logger.Error(err, "failed to get total memory limit") goto unsuitable // If we can't get the memory limit, we assume the node is not suitable @@ -723,11 +723,10 @@ func getScoreUnit(p uint) int { } // getTotalCPURequestRatio returns the total CPU requests (in millicores) ratio for all pods in the namespace. -func (r *DevboxReconciler) getTotalCPURequestRatio(ctx context.Context, namespace string) (float64, error) { +func (r *DevboxReconciler) getTotalCPURequestRatio(ctx context.Context) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ - client.InNamespace(namespace), - client.MatchingFields{"spec.nodeName": r.NodeName}, + client.MatchingFields{devboxv1alpha1.PodNodeNameIndex: r.NodeName}, } if err := r.List(ctx, podList, listOpts...); err != nil { return 0, err @@ -755,11 +754,10 @@ func (r *DevboxReconciler) getTotalCPURequestRatio(ctx context.Context, namespac } // getTotalCPULimitRatio returns the total CPU limits (in millicores) ratio for all pods in the namespace. -func (r *DevboxReconciler) getTotalCPULimitRatio(ctx context.Context, namespace string) (float64, error) { +func (r *DevboxReconciler) getTotalCPULimitRatio(ctx context.Context) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ - client.InNamespace(namespace), - client.MatchingFields{"spec.nodeName": r.NodeName}, + client.MatchingFields{devboxv1alpha1.PodNodeNameIndex: r.NodeName}, } if err := r.List(ctx, podList, listOpts...); err != nil { return 0, err @@ -787,11 +785,10 @@ func (r *DevboxReconciler) getTotalCPULimitRatio(ctx context.Context, namespace } // getTotalMemoryRequestRatio returns the total memory requests ratio for all pods in the namespace. -func (r *DevboxReconciler) getTotalMemoryRequestRatio(ctx context.Context, namespace string) (float64, error) { +func (r *DevboxReconciler) getTotalMemoryRequestRatio(ctx context.Context) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ - client.InNamespace(namespace), - client.MatchingFields{"spec.nodeName": r.NodeName}, + client.MatchingFields{devboxv1alpha1.PodNodeNameIndex: r.NodeName}, } if err := r.List(ctx, podList, listOpts...); err != nil { return 0, err @@ -819,11 +816,10 @@ func (r *DevboxReconciler) getTotalMemoryRequestRatio(ctx context.Context, names } // getTotalMemoryLimitRatio returns the total memory limits ratio for all pods in the namespace. -func (r *DevboxReconciler) getTotalMemoryLimitRatio(ctx context.Context, namespace string) (float64, error) { +func (r *DevboxReconciler) getTotalMemoryLimitRatio(ctx context.Context) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ - client.InNamespace(namespace), - client.MatchingFields{"spec.nodeName": r.NodeName}, + client.MatchingFields{devboxv1alpha1.PodNodeNameIndex: r.NodeName}, } if err := r.List(ctx, podList, listOpts...); err != nil { return 0, err @@ -870,6 +866,15 @@ func (p *ControllerRestartPredicate) Create(e event.CreateEvent) bool { // SetupWithManager sets up the controller with the Manager. func (r *DevboxReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &corev1.Pod{}, devboxv1alpha1.PodNodeNameIndex, func(rawObj client.Object) []string { + pod := rawObj.(*corev1.Pod) + if pod.Spec.NodeName == "" { + return nil + } + return []string{pod.Spec.NodeName} + }); err != nil { + return fmt.Errorf("failed to index field %s: %w", devboxv1alpha1.PodNodeNameIndex, err) + } return ctrl.NewControllerManagedBy(mgr). WithOptions(controller.Options{MaxConcurrentReconciles: 10}). For(&devboxv1alpha1.Devbox{}). From a800c40f376e054070db1846449074594bd70fed Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Fri, 1 Aug 2025 17:29:23 +0800 Subject: [PATCH 13/46] gRPC reconnect and fix container name err (#49) Co-authored-by: Cunzili --- controllers/devbox/internal/commit/commit.go | 135 ++++++++++++++++-- .../devbox/internal/commit/commit_test.go | 48 ++++++- controllers/devbox/internal/commit/const.go | 9 +- 3 files changed, 181 insertions(+), 11 deletions(-) diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 7a71b58663cf..1899fae5fee4 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -6,6 +6,7 @@ import ( "io" "log" "strings" + "time" "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/namespaces" @@ -26,20 +27,40 @@ type Committer interface { type CommitterImpl struct { runtimeServiceClient runtimeapi.RuntimeServiceClient // CRI client containerdClient *client.Client // containerd client + conn *grpc.ClientConn // gRPC connection } // NewCommitter new a CommitterImpl func NewCommitter() (Committer, error) { - // create gRPC connection - conn, err := grpc.NewClient(DefaultContainerdAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - return nil, err + var conn *grpc.ClientConn + var err error + + // retry to connect + for i := 0; i <= DefaultMaxRetries; i++ { + if i > 0 { + log.Printf("Retrying connection to containerd (attempt %d/%d)...", i, DefaultMaxRetries) + time.Sleep(DefaultRetryDelay) + } + + // create gRPC connection + conn, err = grpc.NewClient(DefaultContainerdAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err == nil { + log.Printf("Successfully connected to containerd at %s", DefaultContainerdAddress) + break + } + + log.Printf("Failed to connect to containerd (attempt %d/%d): %v", i+1, DefaultMaxRetries+1, err) + + if i == DefaultMaxRetries { + return nil, fmt.Errorf("failed to connect to containerd after %d attempts: %v", DefaultMaxRetries+1, err) + } } - // create Containerd client: default namespace in const.go + // create Containerd client containerdClient, err := client.NewWithConn(conn, client.WithDefaultNamespace(DefaultNamespace)) if err != nil { - return nil, err + conn.Close() + return nil, fmt.Errorf("failed to create containerd client: %v", err) } // create CRI client @@ -48,6 +69,7 @@ func NewCommitter() (Committer, error) { return &CommitterImpl{ runtimeServiceClient: runtimeServiceClient, containerdClient: containerdClient, + conn: conn, }, nil } @@ -55,6 +77,15 @@ func NewCommitter() (Committer, error) { func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, contentID string, baseImage string) (string, error) { fmt.Println("========>>>> create container", devboxName, contentID, baseImage) ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + + // check connection status, if connection is bad, try to reconnect + if err := c.CheckConnection(ctx); err != nil { + log.Printf("Connection check failed: %v, attempting to reconnect...", err) + if reconnectErr := c.Reconnect(ctx); reconnectErr != nil { + return "", fmt.Errorf("failed to reconnect: %v", reconnectErr) + } + } + global := NewGlobalOptionConfig() // create container with labels @@ -74,7 +105,7 @@ func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, createOpt := types.ContainerCreateOptions{ GOptions: *global, Runtime: DefaultRuntime, // user devbox runtime - Name: fmt.Sprintf("devbox-%s-container", devboxName), + Name: fmt.Sprintf("devbox-%s-container-%d", devboxName,time.Now().Unix()), Pull: "missing", InRun: false, // not start container Rm: false, @@ -120,7 +151,6 @@ func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, // DeleteContainer delete container func (c *CommitterImpl) DeleteContainer(ctx context.Context, containerName string) error { - // load container ctx = namespaces.WithNamespace(ctx, DefaultNamespace) container, err := c.containerdClient.LoadContainer(ctx, containerName) if err != nil { @@ -163,6 +193,15 @@ func (c *CommitterImpl) DeleteContainer(ctx context.Context, containerName strin // RemoveContainer remove container func (c *CommitterImpl) RemoveContainer(ctx context.Context, containerNames string) error { ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + + // check connection status, if connection is bad, try to reconnect + if err := c.CheckConnection(ctx); err != nil { + log.Printf("Connection check failed: %v, attempting to reconnect...", err) + if reconnectErr := c.Reconnect(ctx); reconnectErr != nil { + return fmt.Errorf("failed to reconnect: %v", reconnectErr) + } + } + global := NewGlobalOptionConfig() opt := types.ContainerRemoveOptions{ Stdout: io.Discard, @@ -354,3 +393,83 @@ func NewGlobalOptionConfig() *types.GlobalCommandOptions { DNSSearch: []string{}, } } + +// CheckConnection check if the connection is still alive +func (c *CommitterImpl) CheckConnection(ctx context.Context) error { + if c.conn == nil { + return fmt.Errorf("connection is nil") + } + + // check connection state + state := c.conn.GetState() + if state.String() == "TRANSIENT_FAILURE" || state.String() == "SHUTDOWN" { + return fmt.Errorf("connection is in bad state: %s", state.String()) + } + + // try to ping containerd + _, err := c.containerdClient.Version(ctx) + if err != nil { + return fmt.Errorf("failed to ping containerd: %v", err) + } + + return nil +} + +// Reconnect attempt to reconnect to containerd +func (c *CommitterImpl) Reconnect(ctx context.Context) error { + log.Printf("Attempting to reconnect to containerd...") + + // close old connection + if c.conn != nil { + c.conn.Close() + } + + var conn *grpc.ClientConn + var err error + + for i := 0; i <= DefaultMaxRetries; i++ { + if i > 0 { + log.Printf("Retrying connection to containerd (attempt %d/%d)...", i, DefaultMaxRetries) + time.Sleep(DefaultRetryDelay) + } + + // create gRPC connection + conn, err = grpc.NewClient(DefaultContainerdAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err == nil { + log.Printf("Successfully connected to containerd at %s", DefaultContainerdAddress) + break + } + + log.Printf("Failed to connect to containerd (attempt %d/%d): %v", i+1, DefaultMaxRetries+1, err) + + if i == DefaultMaxRetries { + return fmt.Errorf("failed to connect to containerd after %d attempts: %v", DefaultMaxRetries+1, err) + } + } + + // recreate containerd client + containerdClient, err := client.NewWithConn(conn, client.WithDefaultNamespace(DefaultNamespace)) + if err != nil { + conn.Close() + return fmt.Errorf("failed to recreate containerd client: %v", err) + } + + // recreate CRI client + runtimeServiceClient := runtimeapi.NewRuntimeServiceClient(conn) + + // update instance + c.containerdClient = containerdClient + c.runtimeServiceClient = runtimeServiceClient + c.conn = conn + + log.Printf("Successfully reconnected to containerd") + return nil +} + +// Close close the connection +func (c *CommitterImpl) Close() error { + if c.conn != nil { + return c.conn.Close() + } + return nil +} \ No newline at end of file diff --git a/controllers/devbox/internal/commit/commit_test.go b/controllers/devbox/internal/commit/commit_test.go index 378011dd5688..7c852870069e 100644 --- a/controllers/devbox/internal/commit/commit_test.go +++ b/controllers/devbox/internal/commit/commit_test.go @@ -288,6 +288,52 @@ func TestRuntimeSelection(t *testing.T) { assert.NoError(t, err) } +// test connection management +func TestConnectionManagement(t *testing.T) { + ctx := context.Background() + committer, err := NewCommitter() + assert.NoError(t, err) + defer committer.(*CommitterImpl).Close() + + // test connection check + err = committer.(*CommitterImpl).CheckConnection(ctx) + assert.NoError(t, err) + + // create container + devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) + contentID := "903b3c87-1458-4dd8-b0f4-9da7184cf8ca" + baseImage := "ghcr.io/labring-actions/devbox/go-1.23.0:13aacd8" + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) + assert.NoError(t, err) + assert.NotEmpty(t, containerID) + + // delete container + err = committer.(*CommitterImpl).RemoveContainer(ctx, containerID) + assert.NoError(t, err) + + // test reconnect + err = committer.(*CommitterImpl).Reconnect(ctx) + assert.NoError(t, err) + + // create container again + containerID, err = committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) + assert.NoError(t, err) + assert.NotEmpty(t, containerID) + + // test connection check again + err = committer.(*CommitterImpl).CheckConnection(ctx) + assert.NoError(t, err) + + err = committer.(*CommitterImpl).RemoveContainer(ctx, containerID) + assert.NoError(t, err) + + // test connection check again + err = committer.(*CommitterImpl).CheckConnection(ctx) + assert.NoError(t, err) + + fmt.Printf("Connection management test passed\n") +} + // // test large image // func TestLargeImage(t *testing.T) { // ctx := context.Background() @@ -313,4 +359,4 @@ func TestRuntimeSelection(t *testing.T) { // // clean up // err = committer.(*CommitterImpl).DeleteContainer(ctx, containerID) // assert.NoError(t, err) -// } +// } \ No newline at end of file diff --git a/controllers/devbox/internal/commit/const.go b/controllers/devbox/internal/commit/const.go index 46dc8d3f51ae..b4def9692ca3 100644 --- a/controllers/devbox/internal/commit/const.go +++ b/controllers/devbox/internal/commit/const.go @@ -1,10 +1,12 @@ package commit +import "time" + const ( DefaultNamespace = "sealos.io" DefaultContainerdAddress = "unix:///var/run/containerd/containerd.sock" DefaultDataRoot = "/var/lib/containerd" - DefaultRuntime = "devbox-runc" + DefaultRuntime = "io.containerd.runc.v2" DefaultSnapshotter = "devbox" DefaultNetworkMode = "none" InsecureRegistry = true @@ -18,4 +20,7 @@ const ( SnapshotLabelPrefix = "containerd.io/snapshot/devbox-" ContainerLabelPrefix = "devbox.sealos.io/" -) + + DefaultMaxRetries = 3 + DefaultRetryDelay = 5 * time.Second +) \ No newline at end of file From 4091d76da2b9833b60feb86e87d0f21b53d35825 Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Tue, 5 Aug 2025 14:21:13 +0800 Subject: [PATCH 14/46] Add push image function (#50) * add push image func --------- Co-authored-by: Cunzili --- controllers/devbox/cmd/main.go | 2 +- controllers/devbox/internal/commit/commit.go | 134 +++++++++-- .../devbox/internal/commit/commit_test.go | 208 +++++++++++++----- controllers/devbox/internal/commit/const.go | 2 +- .../controller/state_change_handler.go | 4 +- 5 files changed, 275 insertions(+), 75 deletions(-) diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 876290a7840f..f34a70c90301 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -278,7 +278,7 @@ func main() { os.Exit(1) } - committer, err := commit.NewCommitter() + committer, err := commit.NewCommitter(registryAddr, registryUser, registryPassword) if err != nil { setupLog.Error(err, "unable to create committer") os.Exit(1) diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 1899fae5fee4..a92211584ed2 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -9,9 +9,13 @@ import ( "time" "github.com/containerd/containerd/v2/client" + "github.com/containerd/containerd/v2/core/remotes" + "github.com/containerd/containerd/v2/core/remotes/docker" + "github.com/containerd/containerd/v2/core/remotes/docker/config" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/nerdctl/v2/pkg/api/types" "github.com/containerd/nerdctl/v2/pkg/cmd/container" + "github.com/containerd/nerdctl/v2/pkg/cmd/image" "github.com/containerd/nerdctl/v2/pkg/containerutil" ncdefaults "github.com/containerd/nerdctl/v2/pkg/defaults" "github.com/labring/sealos/controllers/devbox/api/v1alpha1" @@ -21,17 +25,23 @@ import ( ) type Committer interface { - Commit(ctx context.Context, devboxName string, contentID string, baseImage string, commitImage string) error + Commit(ctx context.Context, devboxName string, contentID string, baseImage string, commitImage string) (string, error) + Push(ctx context.Context, imageName string) error + RemoveImage(ctx context.Context, imageName string, force bool, async bool) error + RemoveContainer(ctx context.Context, containerName string) error } type CommitterImpl struct { runtimeServiceClient runtimeapi.RuntimeServiceClient // CRI client containerdClient *client.Client // containerd client conn *grpc.ClientConn // gRPC connection + registryAddr string + registryUsername string + registryPassword string } -// NewCommitter new a CommitterImpl -func NewCommitter() (Committer, error) { +// NewCommitter new a CommitterImpl with registry configuration +func NewCommitter(registryAddr, registryUsername, registryPassword string) (Committer, error) { var conn *grpc.ClientConn var err error @@ -70,6 +80,9 @@ func NewCommitter() (Committer, error) { runtimeServiceClient: runtimeServiceClient, containerdClient: containerdClient, conn: conn, + registryAddr: registryAddr, + registryUsername: registryUsername, + registryPassword: registryPassword, }, nil } @@ -105,7 +118,7 @@ func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, createOpt := types.ContainerCreateOptions{ GOptions: *global, Runtime: DefaultRuntime, // user devbox runtime - Name: fmt.Sprintf("devbox-%s-container-%d", devboxName,time.Now().Unix()), + Name: fmt.Sprintf("devbox-%s-container-%d", devboxName, time.Now().Unix()), Pull: "missing", InRun: false, // not start container Rm: false, @@ -192,6 +205,7 @@ func (c *CommitterImpl) DeleteContainer(ctx context.Context, containerName strin // RemoveContainer remove container func (c *CommitterImpl) RemoveContainer(ctx context.Context, containerNames string) error { + fmt.Println("========>>>> remove container", containerNames) ctx = namespaces.WithNamespace(ctx, DefaultNamespace) // check connection status, if connection is bad, try to reconnect @@ -217,12 +231,12 @@ func (c *CommitterImpl) RemoveContainer(ctx context.Context, containerNames stri } // Commit commit container to image -func (c *CommitterImpl) Commit(ctx context.Context, devboxName string, contentID string, baseImage string, commitImage string) error { +func (c *CommitterImpl) Commit(ctx context.Context, devboxName string, contentID string, baseImage string, commitImage string) (string, error) { fmt.Println("========>>>> commit devbox", devboxName, contentID, baseImage, commitImage) ctx = namespaces.WithNamespace(ctx, DefaultNamespace) containerID, err := c.CreateContainer(ctx, devboxName, contentID, baseImage) if err != nil { - return fmt.Errorf("failed to create container: %v", err) + return "", fmt.Errorf("failed to create container: %v", err) } // create commit options @@ -238,18 +252,40 @@ func (c *CommitterImpl) Commit(ctx context.Context, devboxName string, contentID // commit container err = container.Commit(ctx, c.containerdClient, commitImage, containerID, opt) - // if commit failed, delete container if err != nil { - // delete container - err = c.RemoveContainer(ctx, containerID) - if err != nil { - log.Printf("Warning: failed to delete container %s: %v", containerID, err) - } - return fmt.Errorf("failed to commit container: %v", err) - } - - // commit success, delete container - return c.RemoveContainer(ctx, containerID) + return "", fmt.Errorf("failed to commit container: %v", err) + } + + return containerID, nil + // // if commit failed, delete container + // if err != nil { + // // remove container + // err = c.RemoveContainer(ctx, containerID) + // if err != nil { + // log.Printf("Warning: failed to remove container %s: %v", containerID, err) + // } + // // remove image + // err = c.RemoveImage(ctx, commitImage, false, false) + // if err != nil { + // log.Printf("Warning: failed to remove image %s: %v", commitImage, err) + // } + // return fmt.Errorf("failed to commit container: %v", err) + // } + + // // commit success, push image and delete image + // err = c.Push(ctx, commitImage) + // if err != nil { + // log.Printf("Warning: failed to push image %s: %v", commitImage, err) + // } + // // err = c.RemoveContainer(ctx, containerID) + // // if err != nil { + // // log.Printf("Warning: failed to remove container %s: %v", containerID, err) + // // } + // err = c.RemoveImage(ctx, commitImage, false, false) + // if err != nil { + // log.Printf("Warning: failed to remove image %s: %v", commitImage, err) + // } + // return nil } // GetContainerAnnotations get container annotations @@ -268,6 +304,68 @@ func (c *CommitterImpl) GetContainerAnnotations(ctx context.Context, containerNa return labels, nil } +// Push pushes an image to a remote repository +func (c *CommitterImpl) Push(ctx context.Context, imageName string) error { + fmt.Println("========>>>> push image", imageName) + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + //set resolver + resolver, err := GetResolver(ctx, c.registryUsername, c.registryPassword) + if err != nil { + log.Printf("failed to set resolver, Image: %s, err: %v\n", imageName, err) + return err + } + + imageRef, err := c.containerdClient.GetImage(ctx, imageName) + if err != nil { + log.Printf("failed to get image: %s, err: %v\n", imageName, err) + return err + } + + // push image + err = c.containerdClient.Push(ctx, imageName, imageRef.Target(), + client.WithResolver(resolver), + ) + if err != nil { + log.Printf("failed to push image: %s, err: %v\n", imageName, err) + return err + } + log.Printf("Pushed image success Image: %s\n", imageName) + return nil +} + +// RemoveImage remove image +func (c *CommitterImpl) RemoveImage(ctx context.Context, imageName string, force bool, async bool) error { + fmt.Println("========>>>> remove image", imageName) + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + global := NewGlobalOptionConfig() + opt := types.ImageRemoveOptions{ + Stdout: io.Discard, + GOptions: *global, + Force: force, + Async: async, + } + return image.Remove(ctx, c.containerdClient, []string{imageName}, opt) +} + +func GetResolver(ctx context.Context, username string, secret string) (remotes.Resolver, error) { + resolverOptions := docker.ResolverOptions{ + Tracker: docker.NewInMemoryTracker(), + } + hostOptions := config.HostOptions{} + if username == "" && secret == "" { + hostOptions.Credentials = nil + } else { + // TODO: fix this, use flags or configs to set mulit registry credentials + hostOptions.Credentials = func(host string) (string, string, error) { + return username, secret, nil + } + } + hostOptions.DefaultScheme = "http" + hostOptions.DefaultTLS = nil + resolverOptions.Hosts = config.ConfigureHosts(ctx, hostOptions) + return docker.NewResolver(resolverOptions), nil +} + type GcHandler interface { GC(ctx context.Context) error } @@ -472,4 +570,4 @@ func (c *CommitterImpl) Close() error { return c.conn.Close() } return nil -} \ No newline at end of file +} diff --git a/controllers/devbox/internal/commit/commit_test.go b/controllers/devbox/internal/commit/commit_test.go index 7c852870069e..9bc05f135107 100644 --- a/controllers/devbox/internal/commit/commit_test.go +++ b/controllers/devbox/internal/commit/commit_test.go @@ -12,9 +12,15 @@ import ( "github.com/stretchr/testify/require" ) +const ( + baseImageBusyBox = "docker.io/library/busybox:latest" + baseImageNginx = "docker.io/library/nginx:latest" + baseImageAlpine = "docker.io/library/alpine:latest" +) + // init Committer func TestNewCommitter(t *testing.T) { - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") assert.NoError(t, err) assert.NotNil(t, committer) } @@ -24,31 +30,29 @@ func TestCommitFlow(t *testing.T) { ctx := context.Background() // 1. create committer - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") assert.NoError(t, err) // 2. prepare test data devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) contentID := "test-content-id-123" - baseImage := "docker.io/library/busybox:latest" commitImage := fmt.Sprintf("test-devbox-commit-%d", time.Now().Unix()) // 3. create container and commit container - err = committer.Commit(ctx, devboxName, contentID, baseImage, commitImage) + _, err = committer.Commit(ctx, devboxName, contentID, baseImageBusyBox, commitImage) assert.NoError(t, err) } // test create container func TestCreateContainer(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") assert.NoError(t, err) // create container devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) contentID := fmt.Sprintf("test-content-id-%d", time.Now().Unix()) - baseImage := "docker.io/library/nginx:latest" // use another public image to test - containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImageNginx) assert.NoError(t, err) assert.NotEmpty(t, containerID) @@ -61,12 +65,12 @@ func TestCreateContainer(t *testing.T) { // test delete container func TestDeleteContainer(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") assert.NoError(t, err) // create a container devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) - containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, "test-content-id-789", "docker.io/library/alpine:latest") + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, "test-content-id-789", baseImageAlpine) assert.NoError(t, err) // show all containers in current namespace @@ -101,11 +105,11 @@ func TestDeleteContainer(t *testing.T) { // test remove container func TestRemoveContainer(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") assert.NoError(t, err) // create a container devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) - containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, "test-content-id-789", "docker.io/library/alpine:latest") + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, "test-content-id-789", baseImageAlpine) assert.NoError(t, err) // show all containers in current namespace @@ -140,7 +144,7 @@ func TestRemoveContainer(t *testing.T) { // test error cases func TestErrorCases(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") assert.NoError(t, err) // test use not exist image to create container @@ -159,7 +163,7 @@ func TestErrorCases(t *testing.T) { // test concurrent operations func TestConcurrentOperations(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") assert.NoError(t, err) // concurrent to create container @@ -175,7 +179,7 @@ func TestConcurrentOperations(t *testing.T) { devboxName := fmt.Sprintf("test-devbox-concurrent-%d-%d", time.Now().Unix(), index) containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, fmt.Sprintf("test-content-id-%d", index), - "docker.io/library/busybox:latest") + baseImageBusyBox) if err != nil { t.Errorf("Failed to create container: %v", err) return @@ -208,7 +212,7 @@ func TestConcurrentOperations(t *testing.T) { // test gc handler func TestGCHandler(t *testing.T) { // new Committer and GCHandler - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") require.NoError(t, err) gcHandler := NewGcHandler(committer.(*CommitterImpl).containerdClient) @@ -218,11 +222,11 @@ func TestGCHandler(t *testing.T) { contentID string baseImage string }{ - {"test-gc-devbox-1", "test-gc-content-id-1", "docker.io/library/busybox:latest"}, - {"test-gc-devbox-2", "test-gc-content-id-2", "docker.io/library/nginx:latest"}, - {"test-gc-devbox-3", "test-gc-content-id-3", "docker.io/library/alpine:latest"}, - {"test-gc-devbox-4", "test-gc-content-id-4", "docker.io/library/busybox:latest"}, - {"test-gc-devbox-5", "test-gc-content-id-5", "docker.io/library/nginx:latest"}, + {"test-gc-devbox-1", "test-gc-content-id-1", baseImageBusyBox}, + {"test-gc-devbox-2", "test-gc-content-id-2", baseImageNginx}, + {"test-gc-devbox-3", "test-gc-content-id-3", baseImageAlpine}, + {"test-gc-devbox-4", "test-gc-content-id-4", baseImageBusyBox}, + {"test-gc-devbox-5", "test-gc-content-id-5", baseImageNginx}, } for _, container := range testCountainers { @@ -256,15 +260,14 @@ func TestGCHandler(t *testing.T) { // test runtime selection func TestRuntimeSelection(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") assert.NoError(t, err) // create container with specific runtime devboxName := fmt.Sprintf("test-runtime-%d", time.Now().Unix()) contentID := "test-runtime-content-id" - baseImage := "docker.io/library/busybox:latest" - containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImageBusyBox) assert.NoError(t, err) assert.NotEmpty(t, containerID) @@ -291,7 +294,7 @@ func TestRuntimeSelection(t *testing.T) { // test connection management func TestConnectionManagement(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter() + committer, err := NewCommitter("", "", "") assert.NoError(t, err) defer committer.(*CommitterImpl).Close() @@ -302,8 +305,8 @@ func TestConnectionManagement(t *testing.T) { // create container devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) contentID := "903b3c87-1458-4dd8-b0f4-9da7184cf8ca" - baseImage := "ghcr.io/labring-actions/devbox/go-1.23.0:13aacd8" - containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) + testImage := "ghcr.io/labring-actions/devbox/go-1.23.0:13aacd8" + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, testImage) assert.NoError(t, err) assert.NotEmpty(t, containerID) @@ -316,7 +319,7 @@ func TestConnectionManagement(t *testing.T) { assert.NoError(t, err) // create container again - containerID, err = committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) + containerID, err = committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, testImage) assert.NoError(t, err) assert.NotEmpty(t, containerID) @@ -334,29 +337,128 @@ func TestConnectionManagement(t *testing.T) { fmt.Printf("Connection management test passed\n") } -// // test large image -// func TestLargeImage(t *testing.T) { -// ctx := context.Background() -// committer, err := NewCommitter() -// assert.NoError(t, err) - -// devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) -// contentID := "test-content-id-large" -// baseImage := "docker.io/library/tensorflow/tensorflow:latest" // large image -// commitImage := fmt.Sprintf("localhost:5000/test-large-commit-%d:latest", time.Now().Unix()) - -// // create container -// containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImage) -// if err != nil { -// t.Logf("Skip large image test due to error: %v", err) -// t.Skip() -// } - -// // commit container -// err = committer.Commit(ctx, devboxName, contentID, baseImage, commitImage) -// assert.NoError(t, err) - -// // clean up -// err = committer.(*CommitterImpl).DeleteContainer(ctx, containerID) -// assert.NoError(t, err) -// } \ No newline at end of file +// test push to Docker Hub +func TestPushToDockerHub(t *testing.T) { + ctx := context.Background() + + // use test registry + registryAddr := "docker.io" + registryUser := "cunzili" + registryPassword := "123456789" + + committer, err := NewCommitter(registryAddr, registryUser, registryPassword) + if err != nil { + t.Errorf("Skip Docker Hub push test: failed to create committer: %v", err) + } + + // create a test image name + testImageName := fmt.Sprintf("docker.io/cunzili/cunzili:test-%d", time.Now().Unix()) + + // create a container and commit it to image + devboxName := fmt.Sprintf("test-dockerhub-%d", time.Now().Unix()) + contentID := fmt.Sprintf("test-dockerhub-content-id-%d", time.Now().Unix()) + + containerID, err := committer.Commit(ctx, devboxName, contentID, baseImageBusyBox, testImageName) + if err != nil { + t.Errorf("Skip Docker Hub push test: failed to create test image: %v", err) + } + // containers, err := committer.(*CommitterImpl).containerdClient.Containers(ctx) + // assert.NoError(t, err) + // fmt.Printf("=== All Containers in current namespace ===\n") + // for _, container := range containers { + // fmt.Printf("Container ID: %s\n", container.ID()) + // } + // fmt.Printf("=== Total Containers: %d\n", len(containers)) + + // push to Docker Hub + err = committer.Push(ctx, testImageName) + if err != nil { + t.Errorf("Failed to push image to Docker Hub: %v", err) + } else { + fmt.Printf("Successfully pushed image to Docker Hub: %s\n", testImageName) + fmt.Printf("You can view the image at: https://hub.docker.com/r/cunzili/cunzili/tags\n") + } + + // remove image + err = committer.RemoveImage(ctx, testImageName, false, false) + assert.NoError(t, err) + + // verify image is deleted + _, err = committer.(*CommitterImpl).containerdClient.GetImage(ctx, testImageName) + assert.Error(t, err) + fmt.Println("can not find image:", testImageName) + + // remove container + err = committer.(*CommitterImpl).RemoveContainer(ctx, containerID) + assert.NoError(t, err) + + // verify container is deleted + _, err = committer.(*CommitterImpl).containerdClient.LoadContainer(ctx, containerID) + assert.Error(t, err) + fmt.Println("can not find container:", containerID) +} + +// test push without authentication +func TestPushWithoutAuth(t *testing.T) { + ctx := context.Background() + + // no authentication + registryAddr := "docker.io" + registryUser := "" + registryPassword := "" + + committer, err := NewCommitter(registryAddr, registryUser, registryPassword) + if err != nil { + t.Skipf("Skip no-auth push test: failed to create committer: %v", err) + } + + // use a existing image for test + testImageName := "docker.io/cunzili/cunzili:test-no-auth-1754277739" + + // create a container and commit it to image + devboxName := fmt.Sprintf("test-no-auth-%d", time.Now().Unix()) + contentID := "test-no-auth-content-id" + + _, err = committer.Commit(ctx, devboxName, contentID, baseImageBusyBox, testImageName) + if err != nil { + t.Skipf("Skip no-auth push test: failed to create test image: %v", err) + } + + // test push to Docker Hub (no authentication) + err = committer.Push(ctx, testImageName) + if err != nil { + fmt.Printf("Expected error when pushing without auth: %v\n", err) + // should fail, because it needs authentication + t.Logf("Push failed as expected: %v", err) + } else { + t.Errorf("Push succeeded unexpectedly without authentication") + } +} + +// test remove image +func TestRemoveImage(t *testing.T) { + ctx := context.Background() + committer, err := NewCommitter("", "", "") + assert.NoError(t, err) + + // create a test devbox name and content id + devboxName := fmt.Sprintf("test-remove-devbox-%d", time.Now().Unix()) + contentID := fmt.Sprintf("test-remove-content-id-%d", time.Now().Unix()) + imageName := fmt.Sprintf("test-remove-image-%d", time.Now().Unix()) + + // create a container and commit it to image + _, err = committer.Commit(ctx, devboxName, contentID, baseImageBusyBox, imageName) + assert.NoError(t, err) + + // // push image + // err = committer.Push(ctx, imageName) + // assert.NoError(t, err) + + // remove image + err = committer.(*CommitterImpl).RemoveImage(ctx, imageName, false, false) + assert.NoError(t, err) + + // verify image is deleted + _, err = committer.(*CommitterImpl).containerdClient.GetImage(ctx, imageName) + assert.Error(t, err) +} diff --git a/controllers/devbox/internal/commit/const.go b/controllers/devbox/internal/commit/const.go index b4def9692ca3..417b7a1a653a 100644 --- a/controllers/devbox/internal/commit/const.go +++ b/controllers/devbox/internal/commit/const.go @@ -23,4 +23,4 @@ const ( DefaultMaxRetries = 3 DefaultRetryDelay = 5 * time.Second -) \ No newline at end of file +) diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 483b4da423a1..5a28efd2016e 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -66,7 +66,7 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage h.Logger.Info("commit devbox", "devbox", devbox.Name, "baseImage", baseImage, "commitImage", commitImage) - if err := h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { + if _, err := h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) return err } @@ -107,7 +107,7 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage h.Logger.Info("commit devbox", "devbox", devbox.Name, "baseImage", baseImage, "commitImage", commitImage) - if err := h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { + if _, err := h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) return err } From cbb7e1a79e7669f65422e45c921858e2376961b7 Mon Sep 17 00:00:00 2001 From: yy Date: Wed, 6 Aug 2025 16:16:11 +0800 Subject: [PATCH 15/46] fix registry --- controllers/devbox/internal/commit/commit.go | 24 ++- controllers/devbox/internal/commit/const.go | 5 +- .../controller/state_change_handler.go | 173 ++++++++---------- 3 files changed, 91 insertions(+), 111 deletions(-) diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index a92211584ed2..86a5c760b0cc 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -35,6 +35,7 @@ type CommitterImpl struct { runtimeServiceClient runtimeapi.RuntimeServiceClient // CRI client containerdClient *client.Client // containerd client conn *grpc.ClientConn // gRPC connection + globalOptions *types.GlobalCommandOptions // global options registryAddr string registryUsername string registryPassword string @@ -80,6 +81,7 @@ func NewCommitter(registryAddr, registryUsername, registryPassword string) (Comm runtimeServiceClient: runtimeServiceClient, containerdClient: containerdClient, conn: conn, + globalOptions: NewGlobalOptionConfig(), registryAddr: registryAddr, registryUsername: registryUsername, registryPassword: registryPassword, @@ -99,8 +101,6 @@ func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, } } - global := NewGlobalOptionConfig() - // create container with labels originalAnnotations := map[string]string{ v1alpha1.AnnotationContentID: contentID, @@ -116,7 +116,7 @@ func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, // create container options createOpt := types.ContainerCreateOptions{ - GOptions: *global, + GOptions: *c.globalOptions, Runtime: DefaultRuntime, // user devbox runtime Name: fmt.Sprintf("devbox-%s-container-%d", devboxName, time.Now().Unix()), Pull: "missing", @@ -132,9 +132,7 @@ func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, Label: convertedAnnotations, SnapshotLabels: convertedLabels, ImagePullOpt: types.ImagePullOptions{ - GOptions: types.GlobalCommandOptions{ - Snapshotter: DefaultSnapshotter, - }, + GOptions: *c.globalOptions, }, } @@ -204,8 +202,8 @@ func (c *CommitterImpl) DeleteContainer(ctx context.Context, containerName strin } // RemoveContainer remove container -func (c *CommitterImpl) RemoveContainer(ctx context.Context, containerNames string) error { - fmt.Println("========>>>> remove container", containerNames) +func (c *CommitterImpl) RemoveContainer(ctx context.Context, containerID string) error { + fmt.Println("========>>>> remove container", containerID) ctx = namespaces.WithNamespace(ctx, DefaultNamespace) // check connection status, if connection is bad, try to reconnect @@ -223,7 +221,7 @@ func (c *CommitterImpl) RemoveContainer(ctx context.Context, containerNames stri Volumes: false, GOptions: *global, } - err := container.Remove(ctx, c.containerdClient, []string{containerNames}, opt) + err := container.Remove(ctx, c.containerdClient, []string{containerID}, opt) if err != nil { return fmt.Errorf("failed to remove container: %v", err) } @@ -472,15 +470,15 @@ func NewGlobalOptionConfig() *types.GlobalCommandOptions { return &types.GlobalCommandOptions{ Namespace: DefaultNamespace, Address: DefaultContainerdAddress, - DataRoot: DefaultDataRoot, + DataRoot: DefaultNerdctlDataRoot, Debug: false, DebugFull: false, - Snapshotter: DefaultSnapshotter, + Snapshotter: DefaultDevboxSnapshotter, CNIPath: ncdefaults.CNIPath(), CNINetConfPath: ncdefaults.CNINetConfPath(), CgroupManager: ncdefaults.CgroupManager(), - InsecureRegistry: false, - HostsDir: ncdefaults.HostsDirs(), + InsecureRegistry: InsecureRegistry, + HostsDir: []string{DefaultNerdctlHostsDir}, Experimental: true, HostGatewayIP: ncdefaults.HostGatewayIP(), KubeHideDupe: false, diff --git a/controllers/devbox/internal/commit/const.go b/controllers/devbox/internal/commit/const.go index 417b7a1a653a..e8385ff2f1c4 100644 --- a/controllers/devbox/internal/commit/const.go +++ b/controllers/devbox/internal/commit/const.go @@ -5,9 +5,10 @@ import "time" const ( DefaultNamespace = "sealos.io" DefaultContainerdAddress = "unix:///var/run/containerd/containerd.sock" - DefaultDataRoot = "/var/lib/containerd" DefaultRuntime = "io.containerd.runc.v2" - DefaultSnapshotter = "devbox" + DefaultNerdctlDataRoot = "/var/lib/containerd" + DefaultNerdctlHostsDir = "/etc/containerd/certs.d" + DefaultDevboxSnapshotter = "devbox" DefaultNetworkMode = "none" InsecureRegistry = true PauseContainerDuringCommit = false diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 5a28efd2016e..3b8298ba84a0 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -16,6 +16,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/rand" "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -49,105 +50,85 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er h.Logger.Error(err, "failed to get devbox", "devbox", event.InvolvedObject.Name) return err } - switch devbox.Status.State { - case devboxv1alpha1.DevboxStateRunning: - switch devbox.Spec.State { - case devboxv1alpha1.DevboxStateStopped: - // do not commit, update devbox status state to stopped - devbox.Status.State = devboxv1alpha1.DevboxStateStopped - h.Logger.Info("update devbox status from running to stopped", "devbox", devbox.Name) - if err := h.Client.Status().Update(ctx, devbox); err != nil { - h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) - return err - } - case devboxv1alpha1.DevboxStateShutdown: - // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id - // step 1: do commit - baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage - commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage - h.Logger.Info("commit devbox", "devbox", devbox.Name, "baseImage", baseImage, "commitImage", commitImage) - if _, err := h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { - h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) - return err - } - // step 2: update devbox commit record - devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusSuccess - devbox.Status.CommitRecords[devbox.Status.ContentID].CommitTime = metav1.Now() - // step 3: update devbox status state to shutdown - devbox.Status.State = devboxv1alpha1.DevboxStateShutdown - // step 4: add a new commit record for the new content id - // make sure that always have a new commit record for shutdown state - devbox.Status.ContentID = uuid.New().String() - devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha1.CommitRecord{ - CommitStatus: devboxv1alpha1.CommitStatusPending, - Node: "", - BaseImage: commitImage, - CommitImage: h.generateImageName(devbox), - GenerateTime: metav1.Now(), - } - h.Logger.Info("update devbox status from running to shutdown", "devbox", devbox.Name) - if err := h.Client.Status().Update(ctx, devbox); err != nil { - h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) - return err - } + // Check if state transition is valid and handle accordingly + currentState := devbox.Status.State + targetState := devbox.Spec.State + + // Handle invalid state transition + if currentState == devboxv1alpha1.DevboxStateShutdown && targetState == devboxv1alpha1.DevboxStateStopped { + h.Recorder.Eventf(devbox, corev1.EventTypeWarning, "Shutdown state is not allowed to be changed to stopped state", "Shutdown state is not allowed to be changed to stopped state") + h.Logger.Error(fmt.Errorf("shutdown state is not allowed to be changed to stopped state"), "shutdown state is not allowed to be changed to stopped state", "devbox", devbox.Name) + return fmt.Errorf("shutdown state is not allowed to be changed to stopped state") + } + + // Handle state transitions that require commit + needsCommit := (currentState == devboxv1alpha1.DevboxStateRunning || currentState == devboxv1alpha1.DevboxStateStopped) && targetState == devboxv1alpha1.DevboxStateShutdown + + if needsCommit { + if err := h.commitDevbox(ctx, devbox); err != nil { + h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) + return err } - case devboxv1alpha1.DevboxStateStopped: - switch devbox.Spec.State { - case devboxv1alpha1.DevboxStateRunning: - // do not commit, update devbox status state to running - devbox.Status.State = devboxv1alpha1.DevboxStateRunning - h.Logger.Info("update devbox status from stopped to running", "devbox", devbox.Name) - if err := h.Client.Status().Update(ctx, devbox); err != nil { - h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) - return err - } - case devboxv1alpha1.DevboxStateShutdown: - // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id - // step 1: do commit - baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage - commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage - h.Logger.Info("commit devbox", "devbox", devbox.Name, "baseImage", baseImage, "commitImage", commitImage) - if _, err := h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { - h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) - return err - } - // step 2: update devbox commit record - devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusSuccess - devbox.Status.CommitRecords[devbox.Status.ContentID].CommitTime = metav1.Now() - // step 3: update devbox status state to shutdown - devbox.Status.State = devboxv1alpha1.DevboxStateShutdown - // step 4: add a new commit record for the new content id - // make sure that always have a new commit record for shutdown state - devbox.Status.ContentID = uuid.New().String() - devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha1.CommitRecord{ - CommitStatus: devboxv1alpha1.CommitStatusPending, - Node: "", - BaseImage: commitImage, - CommitImage: h.generateImageName(devbox), - GenerateTime: metav1.Now(), - } - h.Logger.Info("update devbox status from stopped to shutdown", "devbox", devbox.Name) - if err := h.Client.Status().Update(ctx, devbox); err != nil { - h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) - return err - } + } else if currentState != targetState { + // Handle simple state transitions without commit + devbox.Status.State = targetState + h.Logger.Info("update devbox status", "devbox", devbox.Name, "from", currentState, "to", targetState) + if err := h.Client.Status().Update(ctx, devbox); err != nil { + h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) + return err } - case devboxv1alpha1.DevboxStateShutdown: - switch devbox.Spec.State { - case devboxv1alpha1.DevboxStateRunning: - // do not commit, update devbox status state to running - devbox.Status.State = devboxv1alpha1.DevboxStateRunning - h.Logger.Info("update devbox status from shutdown to running", "devbox", devbox.Name) - if err := h.Client.Status().Update(ctx, devbox); err != nil { - h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) - return err - } - case devboxv1alpha1.DevboxStateStopped: - // do nothing, shutdown state is not allowed to be changed to stopped state - h.Recorder.Eventf(devbox, corev1.EventTypeWarning, "Shutdown state is not allowed to be changed to stopped state", "Shutdown state is not allowed to be changed to stopped state") - h.Logger.Error(fmt.Errorf("shutdown state is not allowed to be changed to stopped state"), "shutdown state is not allowed to be changed to stopped state", "devbox", devbox.Name) - return fmt.Errorf("shutdown state is not allowed to be changed to stopped state") + } + return nil +} + +func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha1.Devbox) error { + // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id + // step 1: do commit, push image, remove container whether commit success or not + baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage + commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage + h.Logger.Info("commit devbox", "devbox", devbox.Name, "baseImage", baseImage, "commitImage", commitImage) + var containerID string + var err error + defer func() { + // remove container whether commit success or not + if err := h.Committer.RemoveContainer(ctx, containerID); err != nil { + h.Logger.Error(err, "failed to remove container", "containerID", containerID) } + }() + if containerID, err = h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { + h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) + return err + } + if err := h.Committer.Push(ctx, commitImage); err != nil { + h.Logger.Error(err, "failed to push commit image", "commitImage", commitImage) + return err + } + // remove after push image whether push success + if err := h.Committer.RemoveImage(ctx, commitImage, false, false); err != nil { + h.Logger.Error(err, "failed to remove image", "commitImage", commitImage) + } + // step 2: update devbox commit record + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusSuccess + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitTime = metav1.Now() + // step 3: update devbox status state to shutdown + devbox.Status.State = devboxv1alpha1.DevboxStateShutdown + // step 4: add a new commit record for the new content id + // make sure that always have a new commit record for shutdown state + devbox.Status.ContentID = uuid.New().String() + devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha1.CommitRecord{ + CommitStatus: devboxv1alpha1.CommitStatusPending, + Node: "", + BaseImage: commitImage, + CommitImage: h.generateImageName(devbox), + GenerateTime: metav1.Now(), + } + h.Logger.Info("update devbox status to shutdown", "devbox", devbox.Name) + + if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { + return h.Client.Status().Update(ctx, devbox) + }); err != nil { + h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) + return err } return nil } From 9dc652b7830edbd92ee22c9deda0bbccefd029e0 Mon Sep 17 00:00:00 2001 From: luanshaotong Date: Thu, 7 Aug 2025 15:40:48 +0800 Subject: [PATCH 16/46] update snapshot (#52) --- controllers/devbox/internal/commit/commit.go | 23 ++++++++++++++++++++ controllers/devbox/internal/commit/const.go | 1 + 2 files changed, 24 insertions(+) diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 86a5c760b0cc..c0e0e2b6b90b 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -12,6 +12,7 @@ import ( "github.com/containerd/containerd/v2/core/remotes" "github.com/containerd/containerd/v2/core/remotes/docker" "github.com/containerd/containerd/v2/core/remotes/docker/config" + "github.com/containerd/containerd/v2/core/snapshots" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/nerdctl/v2/pkg/api/types" "github.com/containerd/nerdctl/v2/pkg/cmd/container" @@ -201,6 +202,28 @@ func (c *CommitterImpl) DeleteContainer(ctx context.Context, containerName strin return nil } +func (c *CommitterImpl) SetLvRemovable(ctx context.Context, containerID string, contentID string) error { + fmt.Println("========>>>> set lv removable for container", contentID) + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + + // check connection status, if connection is bad, try to reconnect + if err := c.CheckConnection(ctx); err != nil { + log.Printf("Connection check failed: %v, attempting to reconnect...", err) + if reconnectErr := c.Reconnect(ctx); reconnectErr != nil { + return fmt.Errorf("failed to reconnect: %v", reconnectErr) + } + } + + _, err := c.containerdClient.SnapshotService(DefaultDevboxSnapshotter).Update(ctx, snapshots.Info{ + Name: containerID, + Labels: map[string]string{removeContentIDKey: contentID}, + }, "labels."+removeContentIDKey) + if err != nil { + return err + } + return nil +} + // RemoveContainer remove container func (c *CommitterImpl) RemoveContainer(ctx context.Context, containerID string) error { fmt.Println("========>>>> remove container", containerID) diff --git a/controllers/devbox/internal/commit/const.go b/controllers/devbox/internal/commit/const.go index e8385ff2f1c4..c9e0c5143570 100644 --- a/controllers/devbox/internal/commit/const.go +++ b/controllers/devbox/internal/commit/const.go @@ -21,6 +21,7 @@ const ( SnapshotLabelPrefix = "containerd.io/snapshot/devbox-" ContainerLabelPrefix = "devbox.sealos.io/" + removeContentIDKey = "containerd.io/snapshot/devbox-remove-content-id" DefaultMaxRetries = 3 DefaultRetryDelay = 5 * time.Second From 0e215edf917e666afd0946fee9cc0ea67d04d09a Mon Sep 17 00:00:00 2001 From: yy Date: Mon, 11 Aug 2025 14:48:20 +0800 Subject: [PATCH 17/46] delete pod when pod failed --- controllers/devbox/internal/commit/commit.go | 5 +++-- controllers/devbox/internal/commit/const.go | 2 +- controllers/devbox/internal/controller/devbox_controller.go | 3 +++ .../devbox/internal/controller/state_change_handler.go | 5 ++++- controllers/go.work.sum | 3 +-- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index c0e0e2b6b90b..87c8519d09ea 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -30,6 +30,7 @@ type Committer interface { Push(ctx context.Context, imageName string) error RemoveImage(ctx context.Context, imageName string, force bool, async bool) error RemoveContainer(ctx context.Context, containerName string) error + SetLvRemovable(ctx context.Context, containerID string, contentID string) error } type CommitterImpl struct { @@ -216,8 +217,8 @@ func (c *CommitterImpl) SetLvRemovable(ctx context.Context, containerID string, _, err := c.containerdClient.SnapshotService(DefaultDevboxSnapshotter).Update(ctx, snapshots.Info{ Name: containerID, - Labels: map[string]string{removeContentIDKey: contentID}, - }, "labels."+removeContentIDKey) + Labels: map[string]string{RemoveContentIDkey: contentID}, + }, "labels."+RemoveContentIDkey) if err != nil { return err } diff --git a/controllers/devbox/internal/commit/const.go b/controllers/devbox/internal/commit/const.go index c9e0c5143570..9848f646f972 100644 --- a/controllers/devbox/internal/commit/const.go +++ b/controllers/devbox/internal/commit/const.go @@ -21,7 +21,7 @@ const ( SnapshotLabelPrefix = "containerd.io/snapshot/devbox-" ContainerLabelPrefix = "devbox.sealos.io/" - removeContentIDKey = "containerd.io/snapshot/devbox-remove-content-id" + RemoveContentIDkey = "containerd.io/snapshot/devbox-remove-content-id" DefaultMaxRetries = 3 DefaultRetryDelay = 5 * time.Second diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 74bf58eafb8e..33446a2aa5b1 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -317,6 +317,9 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha1.D if !podList.Items[0].DeletionTimestamp.IsZero() { return r.handlePodDeleted(ctx, &podList.Items[0]) } + if podList.Items[0].Status.Phase != corev1.PodRunning && podList.Items[0].Status.Phase != corev1.PodPending { + return r.deletePod(ctx, &podList.Items[0]) + } return nil default: // more than one pod found, remove finalizer and delete them diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 3b8298ba84a0..510d010d5c08 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -123,13 +123,16 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a GenerateTime: metav1.Now(), } h.Logger.Info("update devbox status to shutdown", "devbox", devbox.Name) - if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { return h.Client.Status().Update(ctx, devbox) }); err != nil { h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) return err } + // step 5: set lv removable + if err := h.Committer.SetLvRemovable(ctx, containerID, devbox.Status.ContentID); err != nil { + h.Logger.Error(err, "failed to set lv removable", "containerID", containerID, "contentID", devbox.Status.ContentID) + } return nil } diff --git a/controllers/go.work.sum b/controllers/go.work.sum index 3a0c46cbfb59..71dd3f9db0c6 100644 --- a/controllers/go.work.sum +++ b/controllers/go.work.sum @@ -2534,6 +2534,7 @@ github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayf github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= +github.com/google/go-containerregistry v0.20.1 h1:eTgx9QNYugV4DN5mz4U8hiAGTi1ybXn0TPi4Smd8du0= github.com/google/go-containerregistry v0.20.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM= github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y= @@ -2868,8 +2869,6 @@ github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2 h1 github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2/go.mod h1:gmUZh2wUVxr/msGogKUi6v9eJbP5ASO4fVYEPzHH4iI= github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 h1:Xd/Ucu301cld+j+E/+nf0aeQ5HOwmZfJ+WH2dCjCy3Y= github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2/go.mod h1:un/Eqg9DrVwazXHukMsxq+PEBixrN7YC3NRXdx5J3tY= -github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27 h1:SkN036GKQ+DOJHVmD3iz2RxggOkxy+suE4ySluf+79Q= -github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27/go.mod h1:3IzoMVp0HIP/lMWRA4xzTMghuKJ4F3fFWTtGAfrFhzk= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= From 54f0f5ddb6ad5bcacb0fd9720e6af8d5ea017c1d Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:37:47 +0800 Subject: [PATCH 18/46] Gc fix (#53) * force gc * force gc update snapshot (#52) fix registry update snapshot (#52) delete pod when pod failed --------- Co-authored-by: Cunzili --- controllers/devbox/cmd/main.go | 5 + controllers/devbox/internal/commit/commit.go | 245 ++++++++------ .../devbox/internal/commit/commit_test.go | 306 +++++++++++++++--- controllers/devbox/internal/commit/const.go | 3 +- 4 files changed, 407 insertions(+), 152 deletions(-) diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index f34a70c90301..8c87acfa65ce 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -284,6 +284,11 @@ func main() { os.Exit(1) } + if err := committer.InitializeGC(context.Background()); err != nil { + setupLog.Error(err, "unable to initialize GC") + os.Exit(1) + } + stateChangeHandler := controller.StateChangeHandler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 87c8519d09ea..17eb444d758e 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -30,6 +30,8 @@ type Committer interface { Push(ctx context.Context, imageName string) error RemoveImage(ctx context.Context, imageName string, force bool, async bool) error RemoveContainer(ctx context.Context, containerName string) error + InitializeGC(ctx context.Context) error + GC(ctx context.Context) error SetLvRemovable(ctx context.Context, containerID string, contentID string) error } @@ -41,6 +43,10 @@ type CommitterImpl struct { registryAddr string registryUsername string registryPassword string + // GC + gcContainerMap map[string]struct{} + gcImageMap map[string]struct{} + gcInterval time.Duration } // NewCommitter new a CommitterImpl with registry configuration @@ -87,6 +93,9 @@ func NewCommitter(registryAddr, registryUsername, registryPassword string) (Comm registryAddr: registryAddr, registryUsername: registryUsername, registryPassword: registryPassword, + gcContainerMap: make(map[string]struct{}), + gcImageMap: make(map[string]struct{}), + gcInterval: DefaultGcInterval, }, nil } @@ -261,6 +270,9 @@ func (c *CommitterImpl) Commit(ctx context.Context, devboxName string, contentID return "", fmt.Errorf("failed to create container: %v", err) } + // // mark for gc + // defer c.MarkForGC(containerID, commitImage) + // create commit options global := NewGlobalOptionConfig() opt := types.ContainerCommitOptions{ @@ -279,35 +291,6 @@ func (c *CommitterImpl) Commit(ctx context.Context, devboxName string, contentID } return containerID, nil - // // if commit failed, delete container - // if err != nil { - // // remove container - // err = c.RemoveContainer(ctx, containerID) - // if err != nil { - // log.Printf("Warning: failed to remove container %s: %v", containerID, err) - // } - // // remove image - // err = c.RemoveImage(ctx, commitImage, false, false) - // if err != nil { - // log.Printf("Warning: failed to remove image %s: %v", commitImage, err) - // } - // return fmt.Errorf("failed to commit container: %v", err) - // } - - // // commit success, push image and delete image - // err = c.Push(ctx, commitImage) - // if err != nil { - // log.Printf("Warning: failed to push image %s: %v", commitImage, err) - // } - // // err = c.RemoveContainer(ctx, containerID) - // // if err != nil { - // // log.Printf("Warning: failed to remove container %s: %v", containerID, err) - // // } - // err = c.RemoveImage(ctx, commitImage, false, false) - // if err != nil { - // log.Printf("Warning: failed to remove image %s: %v", commitImage, err) - // } - // return nil } // GetContainerAnnotations get container annotations @@ -330,6 +313,15 @@ func (c *CommitterImpl) GetContainerAnnotations(ctx context.Context, containerNa func (c *CommitterImpl) Push(ctx context.Context, imageName string) error { fmt.Println("========>>>> push image", imageName) ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + + // check connection status, if connection is bad, try to reconnect + if err := c.CheckConnection(ctx); err != nil { + log.Printf("Connection check failed: %v, attempting to reconnect...", err) + if reconnectErr := c.Reconnect(ctx); reconnectErr != nil { + return fmt.Errorf("failed to reconnect: %v", reconnectErr) + } + } + //set resolver resolver, err := GetResolver(ctx, c.registryUsername, c.registryPassword) if err != nil { @@ -359,6 +351,15 @@ func (c *CommitterImpl) Push(ctx context.Context, imageName string) error { func (c *CommitterImpl) RemoveImage(ctx context.Context, imageName string, force bool, async bool) error { fmt.Println("========>>>> remove image", imageName) ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + + // check connection status, if connection is bad, try to reconnect + if err := c.CheckConnection(ctx); err != nil { + log.Printf("Connection check failed: %v, attempting to reconnect...", err) + if reconnectErr := c.Reconnect(ctx); reconnectErr != nil { + return fmt.Errorf("failed to reconnect: %v", reconnectErr) + } + } + global := NewGlobalOptionConfig() opt := types.ImageRemoveOptions{ Stdout: io.Discard, @@ -369,104 +370,144 @@ func (c *CommitterImpl) RemoveImage(ctx context.Context, imageName string, force return image.Remove(ctx, c.containerdClient, []string{imageName}, opt) } -func GetResolver(ctx context.Context, username string, secret string) (remotes.Resolver, error) { - resolverOptions := docker.ResolverOptions{ - Tracker: docker.NewInMemoryTracker(), - } - hostOptions := config.HostOptions{} - if username == "" && secret == "" { - hostOptions.Credentials = nil - } else { - // TODO: fix this, use flags or configs to set mulit registry credentials - hostOptions.Credentials = func(host string) (string, string, error) { - return username, secret, nil - } - } - hostOptions.DefaultScheme = "http" - hostOptions.DefaultTLS = nil - resolverOptions.Hosts = config.ConfigureHosts(ctx, hostOptions) - return docker.NewResolver(resolverOptions), nil +// MarkForGC mark container and image for GC +func (c *CommitterImpl) MarkForGC(containerID string, imageID string) { + c.gcContainerMap[containerID] = struct{}{} + c.gcImageMap[imageID] = struct{}{} } -type GcHandler interface { - GC(ctx context.Context) error -} - -type Handler struct { - containerdClient *client.Client -} - -func NewGcHandler(containerdClient *client.Client) GcHandler { - return &Handler{ - containerdClient: containerdClient, - } +// GC start periodic GC +func (c *CommitterImpl) GC(ctx context.Context) error { + ticker := time.NewTicker(c.gcInterval) + go func() { + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + log.Printf("Starting periodic GC at: %v", time.Now()) + if err := c.normalGC(ctx); err != nil { + log.Printf("Failed to GC, err: %v", err) + } + } + } + }() + return nil } -// GC gc container -func (h *Handler) GC(ctx context.Context) error { - log.Printf("Starting GC in namespace: %s", DefaultNamespace) +// normalGC gc container and image +func (c *CommitterImpl) normalGC(ctx context.Context) error { + log.Printf("Starting normal GC in namespace: %s", DefaultNamespace) ctx = namespaces.WithNamespace(ctx, DefaultNamespace) // get all container in namespace - containers, err := h.containerdClient.Containers(ctx) + containers, err := c.containerdClient.Containers(ctx) if err != nil { log.Printf("Failed to get containers, err: %v", err) return err } - var deletedContainersCount int + // gc container for _, container := range containers { - // if get container's labels failed, skip - labels, err := container.Labels(ctx) - if err != nil { - log.Printf("Failed to get labels for container %s, err: %v", container.ID(), err) - continue - } - // if container is not devbox container, skip - if _, ok := labels[v1alpha1.AnnotationContentID]; !ok { - continue + if _, ok := c.gcContainerMap[container.ID()]; ok { + err = c.RemoveContainer(ctx, container.ID()) + if err != nil { + log.Printf("Failed to remove container %s, err: %v", container.ID(), err) + } } + } - // get container task - task, err := container.Task(ctx, nil) - if err != nil { - // delete orphan container - log.Printf("Found Orphan Container: %s", container.ID()) - err = container.Delete(ctx, client.WithSnapshotCleanup) + // clear gcContainerMap + c.gcContainerMap = make(map[string]struct{}) + + // get all image in namespace + images, err := c.containerdClient.ListImages(ctx) + if err != nil { + log.Printf("Failed to get images, err: %v", err) + return err + } + + // gc image + for _, image := range images { + if _, ok := c.gcImageMap[image.Name()]; ok { + err = c.RemoveImage(ctx, image.Name(), false, false) if err != nil { - log.Printf("Failed to delete Orphan Container %s, err: %v", container.ID(), err) - } else { - log.Printf("Deleted Orphan Container: %s successfully", container.ID()) - deletedContainersCount++ + log.Printf("Failed to remove image %s, err: %v", image.Name(), err) } - continue } + } - status, err := task.Status(ctx) - if err != nil { - log.Printf("Failed to get task status for container %s, err: %v", container.ID(), err) - continue + // clear gcImageMap + c.gcImageMap = make(map[string]struct{}) + + return nil +} + +// InitializeGC initialize force GC +func (c *CommitterImpl) InitializeGC(ctx context.Context) error { + gcCtx, cancel := context.WithCancel(ctx) + defer cancel() + if err := c.forceGC(gcCtx); err != nil { + log.Printf("Failed to initialize force GC, err: %v", err) + return fmt.Errorf("failed to initialize force GC: %v", err) + } + log.Println("Force GC initialized successfully") + return nil +} + +// forceGC force gc container and image +func (c *CommitterImpl) forceGC(ctx context.Context) error { + log.Printf("Starting force GC in namespace: %s", DefaultNamespace) + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + containers, err := c.containerdClient.Containers(ctx) + if err != nil { + log.Printf("Failed to get containers, err: %v", err) + return err + } + + // gc container + for _, container := range containers { + if err := c.RemoveContainer(ctx, container.ID()); err != nil { + log.Printf("Failed to remove container %s, err: %v", container.ID(), err) } - if status.Status != client.Running { - // delete task - _, err = task.Delete(ctx, client.WithProcessKill) - if err != nil { - log.Printf("Failed to delete task for container %s, err: %v", container.ID(), err) - } + } + c.gcContainerMap = make(map[string]struct{}) - // delete container and snapshot - err = container.Delete(ctx, client.WithSnapshotCleanup) - if err != nil { - log.Printf("Failed to delete container %s, err: %v", container.ID(), err) - } else { - log.Printf("Deleted Container: %s successfully", container.ID()) - deletedContainersCount++ - } + // gc image + images, err := c.containerdClient.ListImages(ctx) + if err != nil { + log.Printf("Failed to get images, err: %v", err) + return err + } + for _, image := range images { + if err := c.RemoveImage(ctx, image.Name(), false, false); err != nil { + log.Printf("Failed to remove image %s, err: %v", image.Name(), err) } } - log.Printf("GC completed, deleted %d containers", deletedContainersCount) + c.gcImageMap = make(map[string]struct{}) return nil } +// GetResolver get resolver +func GetResolver(ctx context.Context, username string, secret string) (remotes.Resolver, error) { + resolverOptions := docker.ResolverOptions{ + Tracker: docker.NewInMemoryTracker(), + } + hostOptions := config.HostOptions{} + if username == "" && secret == "" { + hostOptions.Credentials = nil + } else { + // TODO: fix this, use flags or configs to set mulit registry credentials + hostOptions.Credentials = func(host string) (string, string, error) { + return username, secret, nil + } + } + hostOptions.DefaultScheme = "http" + hostOptions.DefaultTLS = nil + resolverOptions.Hosts = config.ConfigureHosts(ctx, hostOptions) + return docker.NewResolver(resolverOptions), nil +} + // convertLabels convert labels to "containerd.io/snapshot/devbox-" format func convertLabels(labels map[string]string) map[string]string { convertedLabels := make(map[string]string) diff --git a/controllers/devbox/internal/commit/commit_test.go b/controllers/devbox/internal/commit/commit_test.go index 9bc05f135107..ce725a210430 100644 --- a/controllers/devbox/internal/commit/commit_test.go +++ b/controllers/devbox/internal/commit/commit_test.go @@ -7,9 +7,12 @@ import ( "testing" "time" + "sync/atomic" + + "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + // "github.com/stretchr/testify/require" ) const ( @@ -209,54 +212,6 @@ func TestConcurrentOperations(t *testing.T) { fmt.Printf("=== Total %d containers ===\n", len(currentContainers)) } -// test gc handler -func TestGCHandler(t *testing.T) { - // new Committer and GCHandler - committer, err := NewCommitter("", "", "") - require.NoError(t, err) - gcHandler := NewGcHandler(committer.(*CommitterImpl).containerdClient) - - // create 5 containers - testCountainers := []struct { - devboxName string - contentID string - baseImage string - }{ - {"test-gc-devbox-1", "test-gc-content-id-1", baseImageBusyBox}, - {"test-gc-devbox-2", "test-gc-content-id-2", baseImageNginx}, - {"test-gc-devbox-3", "test-gc-content-id-3", baseImageAlpine}, - {"test-gc-devbox-4", "test-gc-content-id-4", baseImageBusyBox}, - {"test-gc-devbox-5", "test-gc-content-id-5", baseImageNginx}, - } - - for _, container := range testCountainers { - _, err := committer.(*CommitterImpl).CreateContainer(context.Background(), container.devboxName, container.contentID, container.baseImage) - require.NoError(t, err) - } - - // show all containers before gc - ctx := namespaces.WithNamespace(context.Background(), DefaultNamespace) - currentContainers, _ := committer.(*CommitterImpl).containerdClient.Containers(ctx) - fmt.Printf("=== All Containers before gc ===\n") - for _, container := range currentContainers { - fmt.Printf("Container ID: %s\n", container.ID()) - } - fmt.Printf("=== Total %d containers ===\n", len(currentContainers)) - - // gc: - err = gcHandler.GC(context.Background()) - require.NoError(t, err) - - // verify containers are deleted - // get current containers list - afterContainers, _ := committer.(*CommitterImpl).containerdClient.Containers(ctx) - fmt.Printf("=== All Containers after gc ===\n") - for _, container := range afterContainers { - fmt.Printf("Container ID: %s\n", container.ID()) - } - fmt.Printf("=== Total %d containers ===\n", len(afterContainers)) -} - // test runtime selection func TestRuntimeSelection(t *testing.T) { ctx := context.Background() @@ -462,3 +417,256 @@ func TestRemoveImage(t *testing.T) { _, err = committer.(*CommitterImpl).containerdClient.GetImage(ctx, imageName) assert.Error(t, err) } + +// TestAtomicLabels test containerd's atomic label update +func TestAtomicLabels(t *testing.T) { + ctx := context.Background() + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + committer, err := NewCommitter("", "", "") + assert.NoError(t, err) + + // 1. create a test container + devboxName := fmt.Sprintf("test-atomic-%d", time.Now().Unix()) + contentID := fmt.Sprintf("test-atomic-content-%d", time.Now().Unix()) + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImageBusyBox) + assert.NoError(t, err) + + // ensure cleanup container after test + defer func() { + err := committer.RemoveContainer(ctx, containerID) + if err != nil { + fmt.Printf("Failed to cleanup container: %v", err) + } + }() + + // 2. get container object + container, err := committer.(*CommitterImpl).containerdClient.LoadContainer(ctx, containerID) + assert.NoError(t, err) + + // 3. concurrent update label count + concurrentUpdates := 10 + successCount := int32(0) + var wg sync.WaitGroup + wg.Add(concurrentUpdates) + + // 4. concurrent update same label + for i := 0; i < concurrentUpdates; i++ { + go func(index int) { + defer wg.Done() + + // get current label + labels, err := container.Labels(ctx) + if err != nil { + fmt.Printf("Failed to get labels: %v", err) + return + } + + // copy label map + newLabels := make(map[string]string) + for k, v := range labels { + newLabels[k] = v + } + + // add or update test label + val1 := fmt.Sprintf("value-%d", index) + val2 := time.Now().Format(time.RFC3339Nano) + newLabels["test-atomic-label"] = val1 + newLabels["timestamp"] = val2 + + // try to update label + _, err = container.SetLabels(ctx, newLabels) + if err != nil { + if errdefs.IsAlreadyExists(err) { + fmt.Printf("Concurrent update detected for index %d\n", index) + } else { + fmt.Printf("Failed to set labels for index %d: %v\n", index, err) + } + return + } + + // update success count + atomic.AddInt32(&successCount, 1) + fmt.Printf("Successfully updated labels for index %d, val1: %s, val2: %s\n", index, val1, val2) + }(i) + } + + // wait for all goroutine + wg.Wait() + + // verify result + finalLabels, err := container.Labels(ctx) + assert.NoError(t, err) + + fmt.Printf("Final label value: %s\n", finalLabels["test-atomic-label"]) + fmt.Printf("Final timestamp: %s\n", finalLabels["timestamp"]) + fmt.Printf("Successful updates: %d/%d\n", successCount, concurrentUpdates) +} + +// test get image +func TestGetImage(t *testing.T) { + ctx := context.Background() + committer, err := NewCommitter("", "", "") + assert.NoError(t, err) + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + images, err := committer.(*CommitterImpl).containerdClient.ListImages(ctx) + assert.NoError(t, err) + for _, image := range images { + fmt.Printf("Image ID: %s\n", image.Target().Digest.String()) + } +} + +// test GC initialization and execution +func TestGCFlow(t *testing.T) { + ctx := context.Background() + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + + // create committer + committer, err := NewCommitter("", "", "") + assert.NoError(t, err) + defer committer.(*CommitterImpl).Close() + + // 1. first execute InitializeGC for initial cleanup + err = committer.InitializeGC(ctx) + assert.NoError(t, err) + + // 2. create some test resources + var containerIDs []string + var imageNames []string + + // create three test containers and images + for i := 0; i < 3; i++ { + devboxName := fmt.Sprintf("test-gc-devbox-%d-%d", time.Now().Unix(), i) + contentID := fmt.Sprintf("test-gc-content-%d", i) + commitImage := fmt.Sprintf("test-gc-image-%d-%d", time.Now().Unix(), i) + + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImageBusyBox) + assert.NoError(t, err) + containerIDs = append(containerIDs, containerID) + imageNames = append(imageNames, commitImage) + } + + // 3. verify resource creation success + containers, err := committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + fmt.Printf("=== Created Containers ===\n") + for _, container := range containers { + fmt.Printf("Container ID: %s\n", container.ID()) + } + + // 4. mark some resources as GC + committer.(*CommitterImpl).MarkForGC(containerIDs[0], imageNames[0]) + committer.(*CommitterImpl).MarkForGC(containerIDs[1], imageNames[1]) + + // 5. execute normal GC + err = committer.(*CommitterImpl).normalGC(ctx) + assert.NoError(t, err) + + // wait for gc + time.Sleep(5 * time.Second) + + // 6. verify marked resources are cleaned up + containers, err = committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + fmt.Printf("=== Containers after normal GC ===\n") + for _, container := range containers { + fmt.Printf("Container ID: %s\n", container.ID()) + // ensure marked containers are deleted + assert.NotEqual(t, containerIDs[0], container.ID()) + assert.NotEqual(t, containerIDs[1], container.ID()) + } + + for i := 0; i < 2; i++ { + devboxName := fmt.Sprintf("test-gc-devbox-%d-%d", time.Now().Unix(), i) + contentID := fmt.Sprintf("test-gc-content-%d", i) + commitImage := fmt.Sprintf("test-gc-image-%d-%d", time.Now().Unix(), i) + + containerID, err := committer.Commit(ctx, devboxName, contentID, baseImageBusyBox, commitImage) + assert.NoError(t, err) + containerIDs = append(containerIDs, containerID) + imageNames = append(imageNames, commitImage) + } + + containers, err = committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + fmt.Printf("===After create 2 more containers ===\n") + for _, container := range containers { + fmt.Printf("Container ID: %s\n", container.ID()) + } + + // 7. execute force GC + err = committer.(*CommitterImpl).forceGC(ctx) + assert.NoError(t, err) + + // 8. verify all resources are cleaned up + containers, err = committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + assert.Equal(t, 0, len(containers), "All containers should be removed after force GC") +} + +// test periodic GC +func TestPeriodicGC(t *testing.T) { + ctx := context.Background() + ctx = namespaces.WithNamespace(ctx, DefaultNamespace) + + // create committer + committer, err := NewCommitter("", "", "") + assert.NoError(t, err) + err = committer.InitializeGC(ctx) + assert.NoError(t, err) + defer committer.(*CommitterImpl).Close() + + // 2. create some test resources + var containerIDs []string + var imageNames []string + + // create four test containers and images + for i := 0; i < 4; i++ { + devboxName := fmt.Sprintf("test-periodic-gc-devbox-%d-%d", time.Now().Unix(), i) + contentID := fmt.Sprintf("test-periodic-gc-content-%d", i) + commitImage := fmt.Sprintf("test-periodic-gc-image-%d-%d", time.Now().Unix(), i) + + containerID, err := committer.(*CommitterImpl).CreateContainer(ctx, devboxName, contentID, baseImageBusyBox) + assert.NoError(t, err) + containerIDs = append(containerIDs, containerID) + imageNames = append(imageNames, commitImage) + } + + // 4. mark some resources as GC + committer.(*CommitterImpl).MarkForGC(containerIDs[0], imageNames[0]) + committer.(*CommitterImpl).MarkForGC(containerIDs[1], imageNames[1]) + + // set short GC interval for test + committer.(*CommitterImpl).gcInterval = 2 * time.Second + // start GC + err = committer.GC(ctx) + assert.NoError(t, err) + + // wait for gc start + fmt.Println("wait for gc start......") + time.Sleep(3 * time.Second) + containers, err := committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + fmt.Printf("=== Containers after first normal GC ===\n") + for _, container := range containers { + fmt.Printf("Container ID: %s\n", container.ID()) + // ensure marked containers are deleted + assert.NotEqual(t, containerIDs[0], container.ID()) + assert.NotEqual(t, containerIDs[1], container.ID()) + } + + committer.(*CommitterImpl).MarkForGC(containerIDs[2], imageNames[2]) + committer.(*CommitterImpl).MarkForGC(containerIDs[3], imageNames[3]) + // wait for gc start again + fmt.Println("wait for gc start again......") + time.Sleep(3 * time.Second) + + // verify resources are cleaned up + containers, err = committer.(*CommitterImpl).containerdClient.Containers(ctx) + assert.NoError(t, err) + + assert.Equal(t, 0, len(containers), "All containers should be removed by periodic GC") + + images, err := committer.(*CommitterImpl).containerdClient.ListImages(ctx) + assert.NoError(t, err) + assert.Equal(t, 0, len(images), "All images should be removed by periodic GC") +} diff --git a/controllers/devbox/internal/commit/const.go b/controllers/devbox/internal/commit/const.go index 9848f646f972..b601e60f2ed2 100644 --- a/controllers/devbox/internal/commit/const.go +++ b/controllers/devbox/internal/commit/const.go @@ -15,9 +15,9 @@ const ( AnnotationKeyNamespace = "namespace" AnnotationKeyImageName = "image.name" - DevboxOptionsRemoveBaseImageTopLayer = true AnnotationImageFromValue = "true" AnnotationUseLimitValue = "1Gi" + DevboxOptionsRemoveBaseImageTopLayer = true SnapshotLabelPrefix = "containerd.io/snapshot/devbox-" ContainerLabelPrefix = "devbox.sealos.io/" @@ -25,4 +25,5 @@ const ( DefaultMaxRetries = 3 DefaultRetryDelay = 5 * time.Second + DefaultGcInterval = 20 * time.Minute ) From 6337c8f7068ead2bbc56e59ec6b9d4e5d067b79e Mon Sep 17 00:00:00 2001 From: yy Date: Mon, 11 Aug 2025 15:52:14 +0800 Subject: [PATCH 19/46] fix containerd errdefs --- controllers/devbox/go.mod | 2 +- controllers/devbox/internal/commit/commit_test.go | 2 +- controllers/go.work.sum | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/controllers/devbox/go.mod b/controllers/devbox/go.mod index cacc6cd80a72..6f246053b14b 100644 --- a/controllers/devbox/go.mod +++ b/controllers/devbox/go.mod @@ -4,6 +4,7 @@ go 1.24.0 require ( github.com/containerd/containerd/v2 v2.1.3 + github.com/containerd/errdefs v1.0.0 github.com/containerd/nerdctl/v2 v2.1.3 github.com/go-logr/logr v1.4.2 github.com/google/uuid v1.6.0 @@ -44,7 +45,6 @@ require ( github.com/containerd/console v1.0.5 // indirect github.com/containerd/containerd/api v1.9.0 // indirect github.com/containerd/continuity v0.4.5 // indirect - github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/go-cni v1.1.12 // indirect diff --git a/controllers/devbox/internal/commit/commit_test.go b/controllers/devbox/internal/commit/commit_test.go index ce725a210430..7bd3357b67f2 100644 --- a/controllers/devbox/internal/commit/commit_test.go +++ b/controllers/devbox/internal/commit/commit_test.go @@ -9,8 +9,8 @@ import ( "sync/atomic" - "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/v2/pkg/namespaces" + "github.com/containerd/errdefs" "github.com/stretchr/testify/assert" // "github.com/stretchr/testify/require" ) diff --git a/controllers/go.work.sum b/controllers/go.work.sum index 71dd3f9db0c6..486284f465f4 100644 --- a/controllers/go.work.sum +++ b/controllers/go.work.sum @@ -2036,6 +2036,7 @@ github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4S github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0= github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= @@ -2107,6 +2108,7 @@ github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJ github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo= github.com/containerd/imgcrypt v1.1.7/go.mod h1:FD8gqIcX5aTotCtOmjeCsi3A1dHmTZpnMISGKSczt4k= +github.com/containerd/imgcrypt v1.1.8 h1:ZS7TuywcRNLoHpU0g+v4/PsKynl6TYlw5xDVWWoIyFA= github.com/containerd/imgcrypt v1.1.8/go.mod h1:x6QvFIkMyO2qGIY2zXc88ivEzcbgvLdWjoZyGqDap5U= 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= @@ -2129,6 +2131,7 @@ github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= From d71e3d5f1c5f5205dfeac0cc1abfb1e2079fc998 Mon Sep 17 00:00:00 2001 From: yy Date: Wed, 13 Aug 2025 14:40:58 +0800 Subject: [PATCH 20/46] fix content id error --- .../devbox/internal/controller/state_change_handler.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 510d010d5c08..9846344cf5e4 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -86,6 +86,7 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a // step 1: do commit, push image, remove container whether commit success or not baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage + oldContentID := devbox.Status.ContentID h.Logger.Info("commit devbox", "devbox", devbox.Name, "baseImage", baseImage, "commitImage", commitImage) var containerID string var err error @@ -130,8 +131,8 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a return err } // step 5: set lv removable - if err := h.Committer.SetLvRemovable(ctx, containerID, devbox.Status.ContentID); err != nil { - h.Logger.Error(err, "failed to set lv removable", "containerID", containerID, "contentID", devbox.Status.ContentID) + if err := h.Committer.SetLvRemovable(ctx, containerID, oldContentID); err != nil { + h.Logger.Error(err, "failed to set lv removable", "containerID", containerID, "contentID", oldContentID) } return nil } From 66eca82f504ad72a2784617f2e039b5247b7835b Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Fri, 15 Aug 2025 11:58:55 +0800 Subject: [PATCH 21/46] add pod annotation (#55) Co-authored-by: Cunzili --- .../devbox/internal/controller/devbox_controller.go | 2 ++ controllers/devbox/internal/controller/helper/devbox.go | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 33446a2aa5b1..3204f933770d 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -24,6 +24,7 @@ import ( "time" devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + "github.com/labring/sealos/controllers/devbox/internal/commit" "github.com/labring/sealos/controllers/devbox/internal/controller/helper" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" @@ -308,6 +309,7 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha1.D helper.WithPodImage(currentRecord.BaseImage), helper.WithPodContentID(devbox.Status.ContentID), helper.WithPodNodeName(currentRecord.Node), + helper.WithPodInit(commit.AnnotationImageFromValue), ) if err := r.Create(ctx, pod); err != nil { return err diff --git a/controllers/devbox/internal/controller/helper/devbox.go b/controllers/devbox/internal/controller/helper/devbox.go index 88880457acdc..271bf98aed2d 100644 --- a/controllers/devbox/internal/controller/helper/devbox.go +++ b/controllers/devbox/internal/controller/helper/devbox.go @@ -54,6 +54,15 @@ func WithPodContentID(contentID string) DevboxPodOptions { } } +func WithPodInit(init string) DevboxPodOptions { + return func(pod *corev1.Pod) { + if pod.Annotations == nil { + pod.Annotations = make(map[string]string) + } + pod.Annotations[devboxv1alpha1.AnnotationInit] = init + } +} + func WithPodAnnotations(annotations map[string]string) DevboxPodOptions { return func(pod *corev1.Pod) { if pod.Annotations == nil { From 2a9a5433fefbca6307a1e30a49b9ed8d57f425f5 Mon Sep 17 00:00:00 2001 From: yy Date: Tue, 19 Aug 2025 16:51:22 +0800 Subject: [PATCH 22/46] add committing status --- .../devbox/api/v1alpha1/devbox_types.go | 9 ++++--- .../crd/bases/devbox.sealos.io_devboxes.yaml | 1 + .../controller/state_change_handler.go | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/controllers/devbox/api/v1alpha1/devbox_types.go b/controllers/devbox/api/v1alpha1/devbox_types.go index 08097da7662d..750897c15da1 100644 --- a/controllers/devbox/api/v1alpha1/devbox_types.go +++ b/controllers/devbox/api/v1alpha1/devbox_types.go @@ -175,9 +175,10 @@ type NetworkStatus struct { type CommitStatus string const ( - CommitStatusSuccess CommitStatus = "Success" - CommitStatusFailed CommitStatus = "Failed" - CommitStatusPending CommitStatus = "Pending" + CommitStatusSuccess CommitStatus = "Success" + CommitStatusFailed CommitStatus = "Failed" + CommitStatusPending CommitStatus = "Pending" + CommitStatusCommitting CommitStatus = "Committing" ) type DevboxPhase string @@ -231,7 +232,7 @@ type CommitRecord struct { CommitTime metav1.Time `json:"commitTime"` // CommitStatus is the status of the commit - // +kubebuilder:validation:Enum=Success;Failed;Pending + // +kubebuilder:validation:Enum=Success;Failed;Pending;Committing // +kubebuilder:default=Pending CommitStatus CommitStatus `json:"commitStatus"` } diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index 7baa32a093a9..b9c236f91eb0 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -3092,6 +3092,7 @@ spec: - Success - Failed - Pending + - Committing type: string commitTime: description: CommitTime is the time when the commit is created diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 9846344cf5e4..5adc569599e7 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -65,6 +65,11 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er needsCommit := (currentState == devboxv1alpha1.DevboxStateRunning || currentState == devboxv1alpha1.DevboxStateStopped) && targetState == devboxv1alpha1.DevboxStateShutdown if needsCommit { + // Check if commit is already in progress to prevent duplicate requests + if devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus == devboxv1alpha1.CommitStatusCommitting { + h.Logger.Info("commit already in progress, skipping duplicate request", "devbox", devbox.Name, "contentID", devbox.Status.ContentID) + return nil + } if err := h.commitDevbox(ctx, devbox); err != nil { h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) return err @@ -83,6 +88,15 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha1.Devbox) error { // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id + // step 0: set commit status to committing to prevent duplicate requests + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusCommitting + devbox.Status.CommitRecords[devbox.Status.ContentID].UpdateTime = metav1.Now() + if err := h.Client.Status().Update(ctx, devbox); err != nil { + h.Logger.Error(err, "failed to update commit status to committing", "devbox", devbox.Name) + return err + } + h.Logger.Info("set commit status to committing", "devbox", devbox.Name, "contentID", devbox.Status.ContentID) + // step 1: do commit, push image, remove container whether commit success or not baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage @@ -98,10 +112,22 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a }() if containerID, err = h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) + // Update commit status to failed on commit error + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusFailed + devbox.Status.CommitRecords[devbox.Status.ContentID].UpdateTime = metav1.Now() + if updateErr := h.Client.Status().Update(ctx, devbox); updateErr != nil { + h.Logger.Error(updateErr, "failed to update commit status to failed", "devbox", devbox.Name) + } return err } if err := h.Committer.Push(ctx, commitImage); err != nil { h.Logger.Error(err, "failed to push commit image", "commitImage", commitImage) + // Update commit status to failed on push error + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusFailed + devbox.Status.CommitRecords[devbox.Status.ContentID].UpdateTime = metav1.Now() + if updateErr := h.Client.Status().Update(ctx, devbox); updateErr != nil { + h.Logger.Error(updateErr, "failed to update commit status to failed", "devbox", devbox.Name) + } return err } // remove after push image whether push success From d46401aaed225a827bdf748a2c9b93bc76a4f476 Mon Sep 17 00:00:00 2001 From: yy Date: Wed, 20 Aug 2025 15:34:59 +0800 Subject: [PATCH 23/46] update go mod --- controllers/devbox/go.mod | 8 ++++---- controllers/devbox/go.sum | 12 ++++++------ controllers/go.work.sum | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/controllers/devbox/go.mod b/controllers/devbox/go.mod index 6f246053b14b..371ab733d6f8 100644 --- a/controllers/devbox/go.mod +++ b/controllers/devbox/go.mod @@ -3,7 +3,7 @@ module github.com/labring/sealos/controllers/devbox go 1.24.0 require ( - github.com/containerd/containerd/v2 v2.1.3 + github.com/containerd/containerd/v2 v2.1.4 github.com/containerd/errdefs v1.0.0 github.com/containerd/nerdctl/v2 v2.1.3 github.com/go-logr/logr v1.4.2 @@ -21,11 +21,11 @@ require ( sigs.k8s.io/controller-runtime v0.20.4 ) -replace github.com/containerd/nerdctl => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27 +replace github.com/containerd/nerdctl => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250820041017-57f3084bf6c6 -replace github.com/containerd/nerdctl/v2 => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27 +replace github.com/containerd/nerdctl/v2 => github.com/luanshaotong/nerdctl/v2 v2.0.0-20250820041017-57f3084bf6c6 -replace github.com/containerd/nerdctl/mod/tigron => github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250731025443-879dbf5c7d27 +replace github.com/containerd/nerdctl/mod/tigron => github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250820041017-57f3084bf6c6 require ( cel.dev/expr v0.20.0 // indirect diff --git a/controllers/devbox/go.sum b/controllers/devbox/go.sum index 15cf98a5b919..424ef6026269 100644 --- a/controllers/devbox/go.sum +++ b/controllers/devbox/go.sum @@ -41,8 +41,8 @@ github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/q github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0= github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI= -github.com/containerd/containerd/v2 v2.1.3 h1:eMD2SLcIQPdMlnlNF6fatlrlRLAeDaiGPGwmRKLZKNs= -github.com/containerd/containerd/v2 v2.1.3/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= +github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ= +github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -232,10 +232,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250731025443-879dbf5c7d27 h1:9fqCrTFVxfoTlRwzJaOemDZBEEQdLHrqbIfOWhjGm1Y= -github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250731025443-879dbf5c7d27/go.mod h1:gmUZh2wUVxr/msGogKUi6v9eJbP5ASO4fVYEPzHH4iI= -github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27 h1:SkN036GKQ+DOJHVmD3iz2RxggOkxy+suE4ySluf+79Q= -github.com/luanshaotong/nerdctl/v2 v2.0.0-20250731025443-879dbf5c7d27/go.mod h1:3IzoMVp0HIP/lMWRA4xzTMghuKJ4F3fFWTtGAfrFhzk= +github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250820041017-57f3084bf6c6 h1:Gvof4KsixCh8vzvashXtXkkHNzOwg/7qZKAqEQ+0eA8= +github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250820041017-57f3084bf6c6/go.mod h1:gmUZh2wUVxr/msGogKUi6v9eJbP5ASO4fVYEPzHH4iI= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250820041017-57f3084bf6c6 h1:+21lTTtGxNsTEDgUyo5jhKVNbz9kgq+2GH8dWjByU2E= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250820041017-57f3084bf6c6/go.mod h1:G559iiQz5CBnDv0dBHJUHyUOCGYwhpjxhkeAZPPXOSQ= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= diff --git a/controllers/go.work.sum b/controllers/go.work.sum index 486284f465f4..fbf16339b26e 100644 --- a/controllers/go.work.sum +++ b/controllers/go.work.sum @@ -1771,6 +1771,7 @@ github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDur github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/Microsoft/hcsshim v0.12.0-rc.0 h1:wX/F5huJxH9APBkhKSEAqaiZsuBvbbDnyBROZAqsSaY= github.com/Microsoft/hcsshim v0.12.0-rc.0/go.mod h1:rvOnw3YlfoNnEp45wReUngvsXbwRW+AFQ10GVjG1kMU= +github.com/Microsoft/hcsshim v0.12.3/go.mod h1:Iyl1WVpZzr+UkzjekHZbV8o5Z9ZkxNGx6CtY2Qg/JVQ= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -2039,6 +2040,7 @@ github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJ github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0= github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE= +github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= @@ -2074,6 +2076,8 @@ github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicH github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= +github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= +github.com/containerd/containerd/v2 v2.1.3/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -2084,6 +2088,8 @@ github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUX github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= github.com/containerd/continuity v0.4.1/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= +github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= @@ -2121,6 +2127,7 @@ github.com/containerd/protobuild v0.3.0/go.mod h1:5mNMFKKAwCIAkFBPiOdtRx2KiQlyEJ github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= +github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= @@ -2128,11 +2135,14 @@ github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8h github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak= +github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= +github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g= github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= @@ -2164,6 +2174,7 @@ github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pA github.com/containers/ocicrypt v1.1.6/go.mod h1:WgjxPWdTJMqYMjf3M6cuIFFA1/MpyyhIM99YInA+Rvc= github.com/containers/ocicrypt v1.1.7/go.mod h1:7CAhjcj2H8AYp5YvEie7oVSK2AhBY8NscCYRawuDNtw= github.com/containers/ocicrypt v1.1.10/go.mod h1:YfzSSr06PTHQwSTUKqDSjish9BeW1E4HUmreluQcMd8= +github.com/containers/storage v1.54.0/go.mod h1:PlMOoinRrBSnhYODLxt4EXl0nmJt+X0kjG0Xdt9fMTw= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -2202,6 +2213,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t 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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -2213,6 +2225,7 @@ github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2 github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.3.5/go.mod h1:edhVd3c6OXKjUmSrVa/tGJRS9joFTxlslFCAyaxigkE= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= @@ -2251,6 +2264,7 @@ github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/ github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= @@ -2820,6 +2834,8 @@ github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= @@ -2980,9 +2996,11 @@ github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2J github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= @@ -3115,6 +3133,7 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.1.0-rc.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.9.1-0.20230317050512-e931285f4b69/go.mod h1:bNpfuSHA3DZRtD0TPWO8LzgtLpFPTVA/3jDkzD/OPyk= @@ -3192,6 +3211,7 @@ github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= @@ -3394,6 +3414,7 @@ github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83 h1:9AUN7+NK4IV+A11igqjQM github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= @@ -3422,6 +3443,7 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= github.com/vishvananda/netlink v1.3.1-0.20250303224720-0e7078ed04c8/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= @@ -3619,6 +3641,7 @@ go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFu go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= @@ -3672,6 +3695,7 @@ go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= @@ -3692,6 +3716,7 @@ go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6 go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= @@ -3719,6 +3744,7 @@ go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5Ukgg go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= @@ -3796,6 +3822,7 @@ golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/ golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= @@ -3895,6 +3922,7 @@ golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -3941,6 +3969,8 @@ golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -4031,6 +4061,8 @@ golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= @@ -4054,6 +4086,7 @@ golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -4445,6 +4478,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240823204242-4ba0660f739c/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= @@ -4499,6 +4533,7 @@ google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDom google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= @@ -4513,6 +4548,7 @@ google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= From b44b21afc382a7dd8a1adaae6c59a6739d22cd75 Mon Sep 17 00:00:00 2001 From: yy Date: Thu, 21 Aug 2025 11:10:52 +0800 Subject: [PATCH 24/46] chanage api to v1alpha2 --- controllers/devbox/PROJECT | 4 +- .../{v1alpha1 => v1alpha2}/devbox_types.go | 2 +- .../groupversion_info.go | 2 +- .../zz_generated.deepcopy.go | 4 +- controllers/devbox/cmd/main.go | 4 +- controllers/devbox/internal/commit/commit.go | 10 +- .../internal/controller/devbox_controller.go | 96 +++++++++---------- .../controller/devbox_controller_test.go | 8 +- .../internal/controller/helper/devbox.go | 54 +++++------ .../controller/state_change_handler.go | 28 +++--- .../devbox/internal/controller/suite_test.go | 4 +- .../controller/utils/matcher/matcher.go | 4 +- 12 files changed, 111 insertions(+), 109 deletions(-) rename controllers/devbox/api/{v1alpha1 => v1alpha2}/devbox_types.go (99%) rename controllers/devbox/api/{v1alpha1 => v1alpha2}/groupversion_info.go (98%) rename controllers/devbox/api/{v1alpha1 => v1alpha2}/zz_generated.deepcopy.go (99%) diff --git a/controllers/devbox/PROJECT b/controllers/devbox/PROJECT index fb2db11da984..23f8331beea7 100644 --- a/controllers/devbox/PROJECT +++ b/controllers/devbox/PROJECT @@ -15,6 +15,6 @@ resources: domain: sealos.io group: devbox kind: Devbox - path: github.com/labring/sealos/controllers/devbox/api/v1alpha1 - version: v1alpha1 + path: github.com/labring/sealos/controllers/devbox/api/v1alpha2 + version: v1alpha2 version: "3" diff --git a/controllers/devbox/api/v1alpha1/devbox_types.go b/controllers/devbox/api/v1alpha2/devbox_types.go similarity index 99% rename from controllers/devbox/api/v1alpha1/devbox_types.go rename to controllers/devbox/api/v1alpha2/devbox_types.go index 750897c15da1..5a257b4eb9fb 100644 --- a/controllers/devbox/api/v1alpha1/devbox_types.go +++ b/controllers/devbox/api/v1alpha2/devbox_types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1alpha1 +package v1alpha2 import ( corev1 "k8s.io/api/core/v1" diff --git a/controllers/devbox/api/v1alpha1/groupversion_info.go b/controllers/devbox/api/v1alpha2/groupversion_info.go similarity index 98% rename from controllers/devbox/api/v1alpha1/groupversion_info.go rename to controllers/devbox/api/v1alpha2/groupversion_info.go index ada13a8d577a..8cc11552321d 100644 --- a/controllers/devbox/api/v1alpha1/groupversion_info.go +++ b/controllers/devbox/api/v1alpha2/groupversion_info.go @@ -17,7 +17,7 @@ limitations under the License. // Package v1alpha1 contains API Schema definitions for the devbox v1alpha1 API group // +kubebuilder:object:generate=true // +groupName=devbox.sealos.io -package v1alpha1 +package v1alpha2 import ( "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go similarity index 99% rename from controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go rename to controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go index f3f49636710c..33f1fccd62b7 100644 --- a/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go +++ b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go @@ -18,10 +18,10 @@ limitations under the License. // Code generated by controller-gen. DO NOT EDIT. -package v1alpha1 +package v1alpha2 import ( - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 8c87acfa65ce..5303498fae4a 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -46,7 +46,7 @@ import ( metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" - devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" "github.com/labring/sealos/controllers/devbox/internal/commit" "github.com/labring/sealos/controllers/devbox/internal/controller" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" @@ -64,7 +64,7 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(devboxv1alpha1.AddToScheme(scheme)) + utilruntime.Must(devboxv1alpha2.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 17eb444d758e..8044653a22e6 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -19,10 +19,12 @@ import ( "github.com/containerd/nerdctl/v2/pkg/cmd/image" "github.com/containerd/nerdctl/v2/pkg/containerutil" ncdefaults "github.com/containerd/nerdctl/v2/pkg/defaults" - "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" + + "github.com/labring/sealos/controllers/devbox/api/v1alpha2" ) type Committer interface { @@ -114,9 +116,9 @@ func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, // create container with labels originalAnnotations := map[string]string{ - v1alpha1.AnnotationContentID: contentID, - v1alpha1.AnnotationInit: AnnotationImageFromValue, - v1alpha1.AnnotationStorageLimit: AnnotationUseLimitValue, + v1alpha2.AnnotationContentID: contentID, + v1alpha2.AnnotationInit: AnnotationImageFromValue, + v1alpha2.AnnotationStorageLimit: AnnotationUseLimitValue, AnnotationKeyNamespace: DefaultNamespace, AnnotationKeyImageName: baseImage, } diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 3204f933770d..216b9b0a0f4d 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -23,7 +23,7 @@ import ( "strconv" "time" - devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" "github.com/labring/sealos/controllers/devbox/internal/commit" "github.com/labring/sealos/controllers/devbox/internal/controller/helper" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" @@ -91,7 +91,7 @@ type DevboxReconciler struct { func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) - devbox := &devboxv1alpha1.Devbox{} + devbox := &devboxv1alpha2.Devbox{} if err := r.Get(ctx, req.NamespacedName, devbox); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } @@ -99,17 +99,17 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr recLabels := label.RecommendedLabels(&label.Recommended{ Name: devbox.Name, ManagedBy: label.DefaultManagedBy, - PartOf: devboxv1alpha1.LabelDevBoxPartOf, + PartOf: devboxv1alpha2.LabelDevBoxPartOf, }) if devbox.ObjectMeta.DeletionTimestamp.IsZero() { // retry add finalizer err := retry.RetryOnConflict(retry.DefaultRetry, func() error { - latestDevbox := &devboxv1alpha1.Devbox{} + latestDevbox := &devboxv1alpha2.Devbox{} if err := r.Get(ctx, req.NamespacedName, latestDevbox); err != nil { return client.IgnoreNotFound(err) } - if controllerutil.AddFinalizer(latestDevbox, devboxv1alpha1.FinalizerName) { + if controllerutil.AddFinalizer(latestDevbox, devboxv1alpha2.FinalizerName) { return r.Update(ctx, latestDevbox) } return nil @@ -124,7 +124,7 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } logger.Info("devbox deleted, remove finalizer") - if controllerutil.RemoveFinalizer(devbox, devboxv1alpha1.FinalizerName) { + if controllerutil.RemoveFinalizer(devbox, devboxv1alpha2.FinalizerName) { if err := r.Update(ctx, devbox); err != nil { return ctrl.Result{}, err } @@ -139,13 +139,13 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } // init devbox status commit record if devbox.Status.CommitRecords[devbox.Status.ContentID] == nil { - devbox.Status.CommitRecords = make(map[string]*devboxv1alpha1.CommitRecord) - devbox.Status.State = devboxv1alpha1.DevboxStateRunning - devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha1.CommitRecord{ + devbox.Status.CommitRecords = make(map[string]*devboxv1alpha2.CommitRecord) + devbox.Status.State = devboxv1alpha2.DevboxStateRunning + devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha2.CommitRecord{ Node: "", BaseImage: devbox.Spec.Image, CommitImage: r.generateImageName(devbox), - CommitStatus: devboxv1alpha1.CommitStatusPending, + CommitStatus: devboxv1alpha2.CommitStatusPending, GenerateTime: metav1.Now(), } } @@ -153,7 +153,7 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // todo: implement the schedule logic to replace the current logic // if devbox state is running, schedule devbox to node, update devbox status and create a new commit record // and filter out the devbox that are not in the current node - if devbox.Spec.State == devboxv1alpha1.DevboxStateRunning { + if devbox.Spec.State == devboxv1alpha2.DevboxStateRunning { if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == "" { if score := r.getAcceptanceScore(ctx, devbox); score >= r.AcceptanceThreshold { // if devbox is not scheduled to node, schedule it to current node @@ -194,7 +194,7 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr logger.Info("sync secret success") r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Sync secret success", "Sync secret success") // create service if network type is NodePort - if devbox.Spec.NetworkSpec.Type == devboxv1alpha1.NetworkTypeNodePort { + if devbox.Spec.NetworkSpec.Type == devboxv1alpha2.NetworkTypeNodePort { logger.Info("syncing service") if err := r.syncService(ctx, devbox, recLabels); err != nil { logger.Error(err, "sync service failed") @@ -226,7 +226,7 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, nil } -func (r *DevboxReconciler) syncSecret(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error { +func (r *DevboxReconciler) syncSecret(ctx context.Context, devbox *devboxv1alpha2.Devbox, recLabels map[string]string) error { objectMeta := metav1.ObjectMeta{ Name: devbox.Name, Namespace: devbox.Namespace, @@ -288,14 +288,14 @@ func (r *DevboxReconciler) syncSecret(ctx context.Context, devbox *devboxv1alpha return nil } -func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error { +func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha2.Devbox, recLabels map[string]string) error { logger := log.FromContext(ctx) podList := &corev1.PodList{} if err := r.List(ctx, podList, client.InNamespace(devbox.Namespace), client.MatchingLabels(recLabels)); err != nil { return err } switch devbox.Spec.State { - case devboxv1alpha1.DevboxStateRunning: + case devboxv1alpha2.DevboxStateRunning: // get pod switch len(podList.Items) { case 0: @@ -332,7 +332,7 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha1.D r.Recorder.Eventf(devbox, corev1.EventTypeWarning, "More than one pod found", "More than one pod found") return fmt.Errorf("more than one pod found") } - case devboxv1alpha1.DevboxStateStopped, devboxv1alpha1.DevboxStateShutdown: + case devboxv1alpha2.DevboxStateStopped, devboxv1alpha2.DevboxStateShutdown: if len(podList.Items) > 0 { for _, pod := range podList.Items { r.deletePod(ctx, &pod) @@ -343,7 +343,7 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha1.D return nil } -func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error { +func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alpha2.Devbox, recLabels map[string]string) error { var servicePorts []corev1.ServicePort // for _, port := range devbox.Spec.Config.Ports { // servicePorts = append(servicePorts, corev1.ServicePort{ @@ -377,17 +377,17 @@ func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alph }, } switch devbox.Spec.State { - case devboxv1alpha1.DevboxStateShutdown: + case devboxv1alpha2.DevboxStateShutdown: err := r.Client.Delete(ctx, service) if err != nil && !errors.IsNotFound(err) { return err } - devbox.Status.Network = devboxv1alpha1.NetworkStatus{ - Type: devboxv1alpha1.NetworkTypeNodePort, + devbox.Status.Network = devboxv1alpha2.NetworkStatus{ + Type: devboxv1alpha2.NetworkTypeNodePort, NodePort: int32(0), } return r.Status().Update(ctx, devbox) - case devboxv1alpha1.DevboxStateRunning, devboxv1alpha1.DevboxStateStopped: + case devboxv1alpha2.DevboxStateRunning, devboxv1alpha2.DevboxStateStopped: if _, err := controllerutil.CreateOrUpdate(ctx, r.Client, service, func() error { // only update some specific fields service.Spec.Selector = expectServiceSpec.Selector @@ -427,7 +427,7 @@ func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alph if nodePort == 0 { return fmt.Errorf("NodePort not found for service %s", service.Name) } - devbox.Status.Network.Type = devboxv1alpha1.NetworkTypeNodePort + devbox.Status.Network.Type = devboxv1alpha2.NetworkTypeNodePort devbox.Status.Network.NodePort = nodePort return r.Status().Update(ctx, devbox) } @@ -435,7 +435,7 @@ func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alph } // sync devbox state, and record the state change event to state change recorder, state change handler will handle the event -func (r *DevboxReconciler) syncDevboxState(ctx context.Context, devbox *devboxv1alpha1.Devbox) bool { +func (r *DevboxReconciler) syncDevboxState(ctx context.Context, devbox *devboxv1alpha2.Devbox) bool { logger := log.FromContext(ctx) logger.Info("syncDevboxState called", "devbox", devbox.Name, @@ -463,7 +463,7 @@ func (r *DevboxReconciler) syncDevboxState(ctx context.Context, devbox *devboxv1 func (r *DevboxReconciler) deletePod(ctx context.Context, pod *corev1.Pod) error { logger := log.FromContext(ctx) // remove finalizer and delete pod - controllerutil.RemoveFinalizer(pod, devboxv1alpha1.FinalizerName) + controllerutil.RemoveFinalizer(pod, devboxv1alpha2.FinalizerName) if err := r.Update(ctx, pod); err != nil { logger.Error(err, "remove finalizer failed") return err @@ -477,7 +477,7 @@ func (r *DevboxReconciler) deletePod(ctx context.Context, pod *corev1.Pod) error func (r *DevboxReconciler) handlePodDeleted(ctx context.Context, pod *corev1.Pod) error { logger := log.FromContext(ctx) - controllerutil.RemoveFinalizer(pod, devboxv1alpha1.FinalizerName) + controllerutil.RemoveFinalizer(pod, devboxv1alpha2.FinalizerName) if err := r.Update(ctx, pod); err != nil { logger.Error(err, "remove finalizer failed") return err @@ -485,19 +485,19 @@ func (r *DevboxReconciler) handlePodDeleted(ctx context.Context, pod *corev1.Pod return nil } -func (r *DevboxReconciler) generateImageName(devbox *devboxv1alpha1.Devbox) string { +func (r *DevboxReconciler) generateImageName(devbox *devboxv1alpha2.Devbox) string { now := time.Now() return fmt.Sprintf("%s/%s/%s:%s-%s", r.CommitImageRegistry, devbox.Namespace, devbox.Name, rand.String(5), now.Format("2006-01-02-150405")) } -func (r *DevboxReconciler) removeAll(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error { +func (r *DevboxReconciler) removeAll(ctx context.Context, devbox *devboxv1alpha2.Devbox, recLabels map[string]string) error { // Delete Pod podList := &corev1.PodList{} if err := r.List(ctx, podList, client.InNamespace(devbox.Namespace), client.MatchingLabels(recLabels)); err != nil { return err } for _, pod := range podList.Items { - if controllerutil.RemoveFinalizer(&pod, devboxv1alpha1.FinalizerName) { + if controllerutil.RemoveFinalizer(&pod, devboxv1alpha2.FinalizerName) { if err := r.Update(ctx, &pod); err != nil { return err } @@ -522,7 +522,7 @@ func (r *DevboxReconciler) deleteResourcesByLabels(ctx context.Context, obj clie return client.IgnoreNotFound(err) } -func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, opts ...helper.DevboxPodOptions) *corev1.Pod { +func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha2.Devbox, opts ...helper.DevboxPodOptions) *corev1.Pod { objectMeta := metav1.ObjectMeta{ Name: devbox.Name, Namespace: devbox.Namespace, @@ -586,7 +586,7 @@ func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, opts } // set controller reference and finalizer _ = controllerutil.SetControllerReference(devbox, expectPod, r.Scheme) - controllerutil.AddFinalizer(expectPod, devboxv1alpha1.FinalizerName) + controllerutil.AddFinalizer(expectPod, devboxv1alpha2.FinalizerName) for _, opt := range opts { opt(expectPod) @@ -602,32 +602,32 @@ func (r *DevboxReconciler) getAcceptanceConsideration(ctx context.Context) (help } ann := node.Annotations ac := helper.AcceptanceConsideration{} - if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationContainerFSAvailableThreshold], 64); err != nil { - logger.Info("failed to parse containerfs available threshold. use default value instead", "value", ann[devboxv1alpha1.AnnotationContainerFSAvailableThreshold]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha2.AnnotationContainerFSAvailableThreshold], 64); err != nil { + logger.Info("failed to parse containerfs available threshold. use default value instead", "value", ann[devboxv1alpha2.AnnotationContainerFSAvailableThreshold]) ac.ContainerFSAvailableThreshold = helper.DefaultContainerFSAvailableThreshold } else { ac.ContainerFSAvailableThreshold = v } - if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationCPURequestRatio], 64); err != nil { - logger.Info("failed to parse CPU request ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationCPURequestRatio]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha2.AnnotationCPURequestRatio], 64); err != nil { + logger.Info("failed to parse CPU request ratio. use default value instead", "value", ann[devboxv1alpha2.AnnotationCPURequestRatio]) ac.CPURequestRatio = helper.DefaultCPURequestRatio } else { ac.CPURequestRatio = v } - if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationCPULimitRatio], 64); err != nil { - logger.Info("failed to parse CPU limit ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationCPULimitRatio]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha2.AnnotationCPULimitRatio], 64); err != nil { + logger.Info("failed to parse CPU limit ratio. use default value instead", "value", ann[devboxv1alpha2.AnnotationCPULimitRatio]) ac.CPULimitRatio = helper.DefaultCPULimitRatio } else { ac.CPULimitRatio = v } - if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationMemoryRequestRatio], 64); err != nil { - logger.Info("failed to parse memory request ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationMemoryRequestRatio]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha2.AnnotationMemoryRequestRatio], 64); err != nil { + logger.Info("failed to parse memory request ratio. use default value instead", "value", ann[devboxv1alpha2.AnnotationMemoryRequestRatio]) ac.MemoryRequestRatio = helper.DefaultMemoryRequestRatio } else { ac.MemoryRequestRatio = v } - if v, err := strconv.ParseFloat(ann[devboxv1alpha1.AnnotationMemoryLimitRatio], 64); err != nil { - logger.Info("failed to parse memory limit ratio. use default value instead", "value", ann[devboxv1alpha1.AnnotationMemoryLimitRatio]) + if v, err := strconv.ParseFloat(ann[devboxv1alpha2.AnnotationMemoryLimitRatio], 64); err != nil { + logger.Info("failed to parse memory limit ratio. use default value instead", "value", ann[devboxv1alpha2.AnnotationMemoryLimitRatio]) ac.MemoryLimitRatio = helper.DefaultMemoryLimitRatio } else { ac.MemoryLimitRatio = v @@ -635,7 +635,7 @@ func (r *DevboxReconciler) getAcceptanceConsideration(ctx context.Context) (help return ac, nil } -func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context, devbox *devboxv1alpha1.Devbox) int { +func (r *DevboxReconciler) getAcceptanceScore(ctx context.Context, devbox *devboxv1alpha2.Devbox) int { logger := log.FromContext(ctx) var ( ac helper.AcceptanceConsideration @@ -731,7 +731,7 @@ func getScoreUnit(p uint) int { func (r *DevboxReconciler) getTotalCPURequestRatio(ctx context.Context) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ - client.MatchingFields{devboxv1alpha1.PodNodeNameIndex: r.NodeName}, + client.MatchingFields{devboxv1alpha2.PodNodeNameIndex: r.NodeName}, } if err := r.List(ctx, podList, listOpts...); err != nil { return 0, err @@ -762,7 +762,7 @@ func (r *DevboxReconciler) getTotalCPURequestRatio(ctx context.Context) (float64 func (r *DevboxReconciler) getTotalCPULimitRatio(ctx context.Context) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ - client.MatchingFields{devboxv1alpha1.PodNodeNameIndex: r.NodeName}, + client.MatchingFields{devboxv1alpha2.PodNodeNameIndex: r.NodeName}, } if err := r.List(ctx, podList, listOpts...); err != nil { return 0, err @@ -793,7 +793,7 @@ func (r *DevboxReconciler) getTotalCPULimitRatio(ctx context.Context) (float64, func (r *DevboxReconciler) getTotalMemoryRequestRatio(ctx context.Context) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ - client.MatchingFields{devboxv1alpha1.PodNodeNameIndex: r.NodeName}, + client.MatchingFields{devboxv1alpha2.PodNodeNameIndex: r.NodeName}, } if err := r.List(ctx, podList, listOpts...); err != nil { return 0, err @@ -824,7 +824,7 @@ func (r *DevboxReconciler) getTotalMemoryRequestRatio(ctx context.Context) (floa func (r *DevboxReconciler) getTotalMemoryLimitRatio(ctx context.Context) (float64, error) { podList := &corev1.PodList{} listOpts := []client.ListOption{ - client.MatchingFields{devboxv1alpha1.PodNodeNameIndex: r.NodeName}, + client.MatchingFields{devboxv1alpha2.PodNodeNameIndex: r.NodeName}, } if err := r.List(ctx, podList, listOpts...); err != nil { return 0, err @@ -871,18 +871,18 @@ func (p *ControllerRestartPredicate) Create(e event.CreateEvent) bool { // SetupWithManager sets up the controller with the Manager. func (r *DevboxReconciler) SetupWithManager(mgr ctrl.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(context.Background(), &corev1.Pod{}, devboxv1alpha1.PodNodeNameIndex, func(rawObj client.Object) []string { + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &corev1.Pod{}, devboxv1alpha2.PodNodeNameIndex, func(rawObj client.Object) []string { pod := rawObj.(*corev1.Pod) if pod.Spec.NodeName == "" { return nil } return []string{pod.Spec.NodeName} }); err != nil { - return fmt.Errorf("failed to index field %s: %w", devboxv1alpha1.PodNodeNameIndex, err) + return fmt.Errorf("failed to index field %s: %w", devboxv1alpha2.PodNodeNameIndex, err) } return ctrl.NewControllerManagedBy(mgr). WithOptions(controller.Options{MaxConcurrentReconciles: 10}). - For(&devboxv1alpha1.Devbox{}). + For(&devboxv1alpha2.Devbox{}). Owns(&corev1.Pod{}, builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})). // enqueue request if pod spec/status is updated Owns(&corev1.Service{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). Owns(&corev1.Secret{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). diff --git a/controllers/devbox/internal/controller/devbox_controller_test.go b/controllers/devbox/internal/controller/devbox_controller_test.go index 12b88993df9d..65dedeaf5e67 100644 --- a/controllers/devbox/internal/controller/devbox_controller_test.go +++ b/controllers/devbox/internal/controller/devbox_controller_test.go @@ -27,7 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" ) var _ = Describe("Devbox Controller", func() { @@ -40,13 +40,13 @@ var _ = Describe("Devbox Controller", func() { Name: resourceName, Namespace: "default", // TODO(user):Modify as needed } - devbox := &devboxv1alpha1.Devbox{} + devbox := &devboxv1alpha2.Devbox{} BeforeEach(func() { By("creating the custom resource for the Kind Devbox") err := k8sClient.Get(ctx, typeNamespacedName, devbox) if err != nil && errors.IsNotFound(err) { - resource := &devboxv1alpha1.Devbox{ + resource := &devboxv1alpha2.Devbox{ ObjectMeta: metav1.ObjectMeta{ Name: resourceName, Namespace: "default", @@ -59,7 +59,7 @@ var _ = Describe("Devbox Controller", func() { AfterEach(func() { // TODO(user): Cleanup logic after each test, like removing the resource instance. - resource := &devboxv1alpha1.Devbox{} + resource := &devboxv1alpha2.Devbox{} err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) diff --git a/controllers/devbox/internal/controller/helper/devbox.go b/controllers/devbox/internal/controller/helper/devbox.go index 271bf98aed2d..d8283e369d55 100644 --- a/controllers/devbox/internal/controller/helper/devbox.go +++ b/controllers/devbox/internal/controller/helper/devbox.go @@ -28,7 +28,7 @@ import ( "k8s.io/utils/ptr" "github.com/google/uuid" - devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" utilsresource "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" "github.com/labring/sealos/controllers/devbox/label" ) @@ -50,7 +50,7 @@ func WithPodContentID(contentID string) DevboxPodOptions { if pod.Annotations == nil { pod.Annotations = make(map[string]string) } - pod.Annotations[devboxv1alpha1.AnnotationContentID] = contentID + pod.Annotations[devboxv1alpha2.AnnotationContentID] = contentID } } @@ -59,7 +59,7 @@ func WithPodInit(init string) DevboxPodOptions { if pod.Annotations == nil { pod.Annotations = make(map[string]string) } - pod.Annotations[devboxv1alpha1.AnnotationInit] = init + pod.Annotations[devboxv1alpha2.AnnotationInit] = init } } @@ -95,7 +95,7 @@ func NewContentID() string { return uuid.New().String() } -func GeneratePodLabels(devbox *devboxv1alpha1.Devbox) map[string]string { +func GeneratePodLabels(devbox *devboxv1alpha2.Devbox) map[string]string { labels := make(map[string]string) if devbox.Spec.Config.Labels != nil { @@ -114,49 +114,49 @@ func GeneratePodLabels(devbox *devboxv1alpha1.Devbox) map[string]string { return labels } -func GeneratePodAnnotations(devbox *devboxv1alpha1.Devbox) map[string]string { +func GeneratePodAnnotations(devbox *devboxv1alpha2.Devbox) map[string]string { annotations := make(map[string]string) if devbox.Spec.Config.Annotations != nil { for k, v := range devbox.Spec.Config.Annotations { annotations[k] = v } } - annotations[devboxv1alpha1.AnnotationStorageLimit] = devbox.Spec.StorageLimit + annotations[devboxv1alpha2.AnnotationStorageLimit] = devbox.Spec.StorageLimit return annotations } -func GenerateDevboxPhase(devbox *devboxv1alpha1.Devbox, podList corev1.PodList) devboxv1alpha1.DevboxPhase { +func GenerateDevboxPhase(devbox *devboxv1alpha2.Devbox, podList corev1.PodList) devboxv1alpha2.DevboxPhase { if len(podList.Items) > 1 { - return devboxv1alpha1.DevboxPhaseError + return devboxv1alpha2.DevboxPhaseError } switch devbox.Spec.State { - case devboxv1alpha1.DevboxStateRunning: + case devboxv1alpha2.DevboxStateRunning: if len(podList.Items) == 0 { - return devboxv1alpha1.DevboxPhasePending + return devboxv1alpha2.DevboxPhasePending } switch podList.Items[0].Status.Phase { case corev1.PodFailed, corev1.PodSucceeded: - return devboxv1alpha1.DevboxPhaseStopped + return devboxv1alpha2.DevboxPhaseStopped case corev1.PodPending: - return devboxv1alpha1.DevboxPhasePending + return devboxv1alpha2.DevboxPhasePending case corev1.PodRunning: if podList.Items[0].Status.ContainerStatuses[0].Ready && podList.Items[0].Status.ContainerStatuses[0].ContainerID != "" { - return devboxv1alpha1.DevboxPhaseRunning + return devboxv1alpha2.DevboxPhaseRunning } - return devboxv1alpha1.DevboxPhasePending + return devboxv1alpha2.DevboxPhasePending } - case devboxv1alpha1.DevboxStateStopped: + case devboxv1alpha2.DevboxStateStopped: if len(podList.Items) == 0 { - return devboxv1alpha1.DevboxPhaseStopped + return devboxv1alpha2.DevboxPhaseStopped } - return devboxv1alpha1.DevboxPhaseStopping - case devboxv1alpha1.DevboxStateShutdown: + return devboxv1alpha2.DevboxPhaseStopping + case devboxv1alpha2.DevboxStateShutdown: if len(podList.Items) == 0 { - return devboxv1alpha1.DevboxPhaseShutdown + return devboxv1alpha2.DevboxPhaseShutdown } - return devboxv1alpha1.DevboxPhaseShutting + return devboxv1alpha2.DevboxPhaseShutting } - return devboxv1alpha1.DevboxPhaseUnknown + return devboxv1alpha2.DevboxPhaseUnknown } func GenerateSSHKeyPair() ([]byte, []byte, error) { @@ -195,7 +195,7 @@ func GenerateSSHVolumeMounts() []corev1.VolumeMount { } // GenerateSSHVolume generates a volume for SSH keys -func GenerateSSHVolume(devbox *devboxv1alpha1.Devbox) corev1.Volume { +func GenerateSSHVolume(devbox *devboxv1alpha2.Devbox) corev1.Volume { return corev1.Volume{ Name: "devbox-ssh-keys", VolumeSource: corev1.VolumeSource{ @@ -218,7 +218,7 @@ func GenerateSSHVolume(devbox *devboxv1alpha1.Devbox) corev1.Volume { } // GenerateResourceRequirements generates the resource requirements for the Devbox pod -func GenerateResourceRequirements(devbox *devboxv1alpha1.Devbox, requestRate utilsresource.RequestRate, ephemeralStorage utilsresource.EphemeralStorage) corev1.ResourceRequirements { +func GenerateResourceRequirements(devbox *devboxv1alpha2.Devbox, requestRate utilsresource.RequestRate, ephemeralStorage utilsresource.EphemeralStorage) corev1.ResourceRequirements { return corev1.ResourceRequirements{ Limits: calculateResourceLimit(devbox.Spec.Resource, ephemeralStorage), Requests: calculateResourceRequest(devbox.Spec.Resource, requestRate, ephemeralStorage), @@ -260,17 +260,17 @@ func calculateResourceRequest(original corev1.ResourceList, requestRate utilsres } // GetWorkingDir get the working directory for the Devbox pod -func GetWorkingDir(devbox *devboxv1alpha1.Devbox) string { +func GetWorkingDir(devbox *devboxv1alpha2.Devbox) string { return devbox.Spec.Config.WorkingDir } // GetCommand get the command for the Devbox pod -func GetCommand(devbox *devboxv1alpha1.Devbox) []string { +func GetCommand(devbox *devboxv1alpha2.Devbox) []string { return devbox.Spec.Config.Command } // GetArgs get the arguments for the Devbox pod -func GetArgs(devbox *devboxv1alpha1.Devbox) []string { +func GetArgs(devbox *devboxv1alpha2.Devbox) []string { return devbox.Spec.Config.Args } @@ -278,7 +278,7 @@ func IsExceededQuotaError(err error) bool { return strings.Contains(err.Error(), "exceeded quota") } -func GetStorageLimitInBytes(devbox *devboxv1alpha1.Devbox) (int64, error) { +func GetStorageLimitInBytes(devbox *devboxv1alpha2.Devbox) (int64, error) { if devbox.Spec.StorageLimit != "" { storageLimit, err := resource.ParseQuantity(devbox.Spec.StorageLimit) if err != nil { diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 5adc569599e7..03b1d6de84e0 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -7,7 +7,7 @@ import ( "github.com/go-logr/logr" "github.com/google/uuid" - devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" "github.com/labring/sealos/controllers/devbox/internal/commit" corev1 "k8s.io/api/core/v1" @@ -45,7 +45,7 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er h.Logger.Info("event source host is not the node name, skip", "event", event) return nil } - devbox := &devboxv1alpha1.Devbox{} + devbox := &devboxv1alpha2.Devbox{} if err := h.Client.Get(ctx, types.NamespacedName{Namespace: event.Namespace, Name: event.InvolvedObject.Name}, devbox); err != nil { h.Logger.Error(err, "failed to get devbox", "devbox", event.InvolvedObject.Name) return err @@ -55,18 +55,18 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er targetState := devbox.Spec.State // Handle invalid state transition - if currentState == devboxv1alpha1.DevboxStateShutdown && targetState == devboxv1alpha1.DevboxStateStopped { + if currentState == devboxv1alpha2.DevboxStateShutdown && targetState == devboxv1alpha2.DevboxStateStopped { h.Recorder.Eventf(devbox, corev1.EventTypeWarning, "Shutdown state is not allowed to be changed to stopped state", "Shutdown state is not allowed to be changed to stopped state") h.Logger.Error(fmt.Errorf("shutdown state is not allowed to be changed to stopped state"), "shutdown state is not allowed to be changed to stopped state", "devbox", devbox.Name) return fmt.Errorf("shutdown state is not allowed to be changed to stopped state") } // Handle state transitions that require commit - needsCommit := (currentState == devboxv1alpha1.DevboxStateRunning || currentState == devboxv1alpha1.DevboxStateStopped) && targetState == devboxv1alpha1.DevboxStateShutdown + needsCommit := (currentState == devboxv1alpha2.DevboxStateRunning || currentState == devboxv1alpha2.DevboxStateStopped) && targetState == devboxv1alpha2.DevboxStateShutdown if needsCommit { // Check if commit is already in progress to prevent duplicate requests - if devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus == devboxv1alpha1.CommitStatusCommitting { + if devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus == devboxv1alpha2.CommitStatusCommitting { h.Logger.Info("commit already in progress, skipping duplicate request", "devbox", devbox.Name, "contentID", devbox.Status.ContentID) return nil } @@ -86,10 +86,10 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er return nil } -func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha1.Devbox) error { +func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha2.Devbox) error { // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id // step 0: set commit status to committing to prevent duplicate requests - devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusCommitting + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha2.CommitStatusCommitting devbox.Status.CommitRecords[devbox.Status.ContentID].UpdateTime = metav1.Now() if err := h.Client.Status().Update(ctx, devbox); err != nil { h.Logger.Error(err, "failed to update commit status to committing", "devbox", devbox.Name) @@ -113,7 +113,7 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a if containerID, err = h.Committer.Commit(ctx, devbox.Name, devbox.Status.ContentID, baseImage, commitImage); err != nil { h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) // Update commit status to failed on commit error - devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusFailed + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha2.CommitStatusFailed devbox.Status.CommitRecords[devbox.Status.ContentID].UpdateTime = metav1.Now() if updateErr := h.Client.Status().Update(ctx, devbox); updateErr != nil { h.Logger.Error(updateErr, "failed to update commit status to failed", "devbox", devbox.Name) @@ -123,7 +123,7 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a if err := h.Committer.Push(ctx, commitImage); err != nil { h.Logger.Error(err, "failed to push commit image", "commitImage", commitImage) // Update commit status to failed on push error - devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusFailed + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha2.CommitStatusFailed devbox.Status.CommitRecords[devbox.Status.ContentID].UpdateTime = metav1.Now() if updateErr := h.Client.Status().Update(ctx, devbox); updateErr != nil { h.Logger.Error(updateErr, "failed to update commit status to failed", "devbox", devbox.Name) @@ -135,15 +135,15 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a h.Logger.Error(err, "failed to remove image", "commitImage", commitImage) } // step 2: update devbox commit record - devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha1.CommitStatusSuccess + devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha2.CommitStatusSuccess devbox.Status.CommitRecords[devbox.Status.ContentID].CommitTime = metav1.Now() // step 3: update devbox status state to shutdown - devbox.Status.State = devboxv1alpha1.DevboxStateShutdown + devbox.Status.State = devboxv1alpha2.DevboxStateShutdown // step 4: add a new commit record for the new content id // make sure that always have a new commit record for shutdown state devbox.Status.ContentID = uuid.New().String() - devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha1.CommitRecord{ - CommitStatus: devboxv1alpha1.CommitStatusPending, + devbox.Status.CommitRecords[devbox.Status.ContentID] = &devboxv1alpha2.CommitRecord{ + CommitStatus: devboxv1alpha2.CommitStatusPending, Node: "", BaseImage: commitImage, CommitImage: h.generateImageName(devbox), @@ -163,7 +163,7 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a return nil } -func (h *StateChangeHandler) generateImageName(devbox *devboxv1alpha1.Devbox) string { +func (h *StateChangeHandler) generateImageName(devbox *devboxv1alpha2.Devbox) string { now := time.Now() return fmt.Sprintf("%s/%s/%s:%s-%s", h.CommitImageRegistry, devbox.Namespace, devbox.Name, rand.String(5), now.Format("2006-01-02-150405")) } diff --git a/controllers/devbox/internal/controller/suite_test.go b/controllers/devbox/internal/controller/suite_test.go index 0035ea039c1c..0b9104ffcc5b 100644 --- a/controllers/devbox/internal/controller/suite_test.go +++ b/controllers/devbox/internal/controller/suite_test.go @@ -32,7 +32,7 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" // +kubebuilder:scaffold:imports ) @@ -72,7 +72,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - err = devboxv1alpha1.AddToScheme(scheme.Scheme) + err = devboxv1alpha2.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) // +kubebuilder:scaffold:scheme diff --git a/controllers/devbox/internal/controller/utils/matcher/matcher.go b/controllers/devbox/internal/controller/utils/matcher/matcher.go index 54d5d94a6e6d..8e69a8ef6373 100644 --- a/controllers/devbox/internal/controller/utils/matcher/matcher.go +++ b/controllers/devbox/internal/controller/utils/matcher/matcher.go @@ -19,7 +19,7 @@ import ( corev1 "k8s.io/api/core/v1" - devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" ) type PodMatcher interface { @@ -145,7 +145,7 @@ func (p PortMatcher) Match(expectPod *corev1.Pod, pod *corev1.Pod) bool { type StorageLimitMatcher struct{} func (s StorageLimitMatcher) Match(expectPod *corev1.Pod, pod *corev1.Pod) bool { - return expectPod.ObjectMeta.Annotations[devboxv1alpha1.AnnotationStorageLimit] == pod.ObjectMeta.Annotations[devboxv1alpha1.AnnotationStorageLimit] + return expectPod.ObjectMeta.Annotations[devboxv1alpha2.AnnotationStorageLimit] == pod.ObjectMeta.Annotations[devboxv1alpha2.AnnotationStorageLimit] } // PredicateCommitStatus returns the commit status of the pod From 87ec9bcfad35949fe6a56258b68b51343827a990 Mon Sep 17 00:00:00 2001 From: yy Date: Thu, 21 Aug 2025 11:11:18 +0800 Subject: [PATCH 25/46] chanage api to v1alpha2 --- .../devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index b9c236f91eb0..2478e2656c44 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -27,7 +27,7 @@ spec: - jsonPath: .status.phase name: Phase type: string - name: v1alpha1 + name: v1alpha2 schema: openAPIV3Schema: description: Devbox is the Schema for the devboxes API From c5259c57e5e61a422bfdf34450e95129d3b5b574 Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:26:53 +0800 Subject: [PATCH 26/46] Add remove check and fix container name error (#56) * resolve conflict * fix container name and add remove containerID check --------- Co-authored-by: Cunzili --- controllers/devbox/internal/commit/commit.go | 7 ++++++- controllers/go.work.sum | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 8044653a22e6..3b762d947200 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -131,7 +131,7 @@ func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, createOpt := types.ContainerCreateOptions{ GOptions: *c.globalOptions, Runtime: DefaultRuntime, // user devbox runtime - Name: fmt.Sprintf("devbox-%s-container-%d", devboxName, time.Now().Unix()), + Name: fmt.Sprintf("devbox-%s-container-%d", devboxName, time.Now().UnixMicro()), Pull: "missing", InRun: false, // not start container Rm: false, @@ -238,6 +238,11 @@ func (c *CommitterImpl) SetLvRemovable(ctx context.Context, containerID string, // RemoveContainer remove container func (c *CommitterImpl) RemoveContainer(ctx context.Context, containerID string) error { + // check containerID is not empty + if containerID == "" { + return fmt.Errorf("[RemoveContainer]containerID is empty") + } + fmt.Println("========>>>> remove container", containerID) ctx = namespaces.WithNamespace(ctx, DefaultNamespace) diff --git a/controllers/go.work.sum b/controllers/go.work.sum index fbf16339b26e..d467e1fe7aaf 100644 --- a/controllers/go.work.sum +++ b/controllers/go.work.sum @@ -2077,7 +2077,8 @@ github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0 github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= -github.com/containerd/containerd/v2 v2.1.3/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= +github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ= +github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -2174,6 +2175,7 @@ github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pA github.com/containers/ocicrypt v1.1.6/go.mod h1:WgjxPWdTJMqYMjf3M6cuIFFA1/MpyyhIM99YInA+Rvc= github.com/containers/ocicrypt v1.1.7/go.mod h1:7CAhjcj2H8AYp5YvEie7oVSK2AhBY8NscCYRawuDNtw= github.com/containers/ocicrypt v1.1.10/go.mod h1:YfzSSr06PTHQwSTUKqDSjish9BeW1E4HUmreluQcMd8= +github.com/containers/storage v1.54.0 h1:xwYAlf6n9OnIlURQLLg3FYHbO74fQ/2W2N6EtQEUM4I= github.com/containers/storage v1.54.0/go.mod h1:PlMOoinRrBSnhYODLxt4EXl0nmJt+X0kjG0Xdt9fMTw= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -2888,6 +2890,8 @@ github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2 h1 github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2/go.mod h1:gmUZh2wUVxr/msGogKUi6v9eJbP5ASO4fVYEPzHH4iI= github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 h1:Xd/Ucu301cld+j+E/+nf0aeQ5HOwmZfJ+WH2dCjCy3Y= github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2/go.mod h1:un/Eqg9DrVwazXHukMsxq+PEBixrN7YC3NRXdx5J3tY= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250820041017-57f3084bf6c6 h1:+21lTTtGxNsTEDgUyo5jhKVNbz9kgq+2GH8dWjByU2E= +github.com/luanshaotong/nerdctl/v2 v2.0.0-20250820041017-57f3084bf6c6/go.mod h1:G559iiQz5CBnDv0dBHJUHyUOCGYwhpjxhkeAZPPXOSQ= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= @@ -3414,6 +3418,7 @@ github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83 h1:9AUN7+NK4IV+A11igqjQM github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -3437,6 +3442,7 @@ github.com/vbauerster/mpb/v8 v8.4.0/go.mod h1:vjp3hSTuCtR+x98/+2vW3eZ8XzxvGoP8CP github.com/vektah/gqlparser/v2 v2.4.5/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0= github.com/veraison/go-cose v1.0.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= github.com/veraison/go-cose v1.1.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= +github.com/veraison/go-cose v1.2.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= From bb5eacd195ea93dd5204a0fb50f71a823174b43b Mon Sep 17 00:00:00 2001 From: yy Date: Thu, 21 Aug 2025 15:26:59 +0800 Subject: [PATCH 27/46] add ports --- .../devbox/api/v1alpha2/devbox_types.go | 18 +- .../api/v1alpha2/zz_generated.deepcopy.go | 21 ++- .../crd/bases/devbox.sealos.io_devboxes.yaml | 162 ++++++++++++++++++ 3 files changed, 192 insertions(+), 9 deletions(-) diff --git a/controllers/devbox/api/v1alpha2/devbox_types.go b/controllers/devbox/api/v1alpha2/devbox_types.go index 5a257b4eb9fb..db94601a4fe4 100644 --- a/controllers/devbox/api/v1alpha2/devbox_types.go +++ b/controllers/devbox/api/v1alpha2/devbox_types.go @@ -53,9 +53,11 @@ const ( DevboxStateRunning DevboxState = "Running" // DevboxStatePending means the Devbox is pending DevboxStatePending DevboxState = "Pending" - // DevboxStateStopped means the Devbox is stopped + // DevboxStatePaused means the Devbox is paused, pod will be released + DevboxStatePaused DevboxState = "Paused" + // DevboxStateStopped means the Devbox is stopped, pod and content lv will be released DevboxStateStopped DevboxState = "Stopped" - // DevboxStateShutdown means the devbox is shutdown + // DevboxStateShutdown means the devbox is shutdown, pod, content lv and nodeport service will be released DevboxStateShutdown DevboxState = "Shutdown" ) @@ -78,7 +80,7 @@ type NetworkSpec struct { // +kubebuilder:validation:Enum=NodePort;Tailnet Type NetworkType `json:"type"` // // +kubebuilder:validation:Optional - // ExtraPorts []corev1.ContainerPort `json:"extraPorts,omitempty"` + ExtraPorts []corev1.ContainerPort `json:"extraPorts,omitempty"` } type Config struct { @@ -109,10 +111,12 @@ type Config struct { ReleaseArgs []string `json:"releaseArgs,omitempty"` // TODO: in v1alpha2 api we need fix the port and app port into one field and create a new type for it. - // // +kubebuilder:validation:Optional - // Ports []corev1.ContainerPort `json:"ports,omitempty"` - // // +kubebuilder:validation:Optional - // AppPorts []corev1.ServicePort `json:"appPorts,omitempty"` + // +kubebuilder:validation:Optional + // +kubebuilder:default={{name:"devbox-ssh-port",containerPort:22,protocol:TCP}} + Ports []corev1.ContainerPort `json:"ports,omitempty"` + // +kubebuilder:validation:Optional + // +kubebuilder:default={{name:"devbox-app-port",port:8080,protocol:TCP}} + AppPorts []corev1.ServicePort `json:"appPorts,omitempty"` // +kubebuilder:validation:Optional VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` diff --git a/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go index 33f1fccd62b7..769dc5365290 100644 --- a/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go +++ b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go @@ -21,7 +21,7 @@ limitations under the License. package v1alpha2 import ( - v1 "k8s.io/api/core/v1" + "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -118,6 +118,18 @@ func (in *Config) DeepCopyInto(out *Config) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]v1.ContainerPort, len(*in)) + copy(*out, *in) + } + if in.AppPorts != nil { + in, out := &in.AppPorts, &out.AppPorts + *out = make([]v1.ServicePort, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.VolumeMounts != nil { in, out := &in.VolumeMounts, &out.VolumeMounts *out = make([]v1.VolumeMount, len(*in)) @@ -214,7 +226,7 @@ func (in *DevboxSpec) DeepCopyInto(out *DevboxSpec) { } } in.Config.DeepCopyInto(&out.Config) - out.NetworkSpec = in.NetworkSpec + in.NetworkSpec.DeepCopyInto(&out.NetworkSpec) if in.NodeSelector != nil { in, out := &in.NodeSelector, &out.NodeSelector *out = make(map[string]string, len(*in)) @@ -281,6 +293,11 @@ func (in *DevboxStatus) DeepCopy() *DevboxStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) { *out = *in + if in.ExtraPorts != nil { + in, out := &in.ExtraPorts, &out.ExtraPorts + *out = make([]v1.ContainerPort, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkSpec. diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index 2478e2656c44..7af59073cb17 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -978,6 +978,83 @@ spec: additionalProperties: type: string type: object + appPorts: + default: + - name: devbox-app-port + port: 8080 + protocol: TCP + items: + description: ServicePort contains information on service's port. + properties: + appProtocol: + description: |- + The application protocol for this port. + This is used as a hint for implementations to offer richer behavior for protocols that they understand. + This field follows standard Kubernetes label syntax. + Valid values are either: + + * Un-prefixed protocol names - reserved for IANA standard service names (as per + RFC-6335 and https://www.iana.org/assignments/service-names). + + * Kubernetes-defined prefixed names: + * 'kubernetes.io/h2c' - HTTP/2 prior knowledge over cleartext as described in https://www.rfc-editor.org/rfc/rfc9113.html#name-starting-http-2-with-prior- + * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455 + * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455 + + * Other protocols should use implementation-defined prefixed names such as + mycompany.com/my-custom-protocol. + type: string + name: + description: |- + The name of this port within the service. This must be a DNS_LABEL. + All ports within a ServiceSpec must have unique names. When considering + the endpoints for a Service, this must match the 'name' field in the + EndpointPort. + Optional if only one ServicePort is defined on this service. + type: string + nodePort: + description: |- + The port on each node on which this service is exposed when type is + NodePort or LoadBalancer. Usually assigned by the system. If a value is + specified, in-range, and not in use it will be used, otherwise the + operation will fail. If not specified, a port will be allocated if this + Service requires one. If this field is specified when creating a + Service which does not need it, creation will fail. This field will be + wiped when updating a Service to no longer need it (e.g. changing type + from NodePort to ClusterIP). + More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + allOf: + - default: TCP + - default: TCP + description: |- + The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". + Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the pods targeted by the service. + Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named port in the + target Pod's container ports. If this is not specified, the value + of the 'port' field is used (an identity map). + This field is ignored for services with clusterIP=None, and should be + omitted or set equal to the 'port' field. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array args: description: kubebuilder:validation:Optional items: @@ -1110,6 +1187,50 @@ spec: additionalProperties: type: string type: object + ports: + default: + - containerPort: 22 + name: devbox-ssh-port + protocol: TCP + items: + description: ContainerPort represents a network port in a single + container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + allOf: + - default: TCP + - default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array releaseArgs: default: - /home/devbox/project/entrypoint.sh @@ -2987,6 +3108,47 @@ spec: type: string network: properties: + extraPorts: + description: // +kubebuilder:validation:Optional + items: + description: ContainerPort represents a network port in a single + container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + allOf: + - default: TCP + - default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array type: enum: - NodePort From 709e45fc0225d9670cdcd95675a46854294a117c Mon Sep 17 00:00:00 2001 From: yy Date: Fri, 22 Aug 2025 10:43:38 +0800 Subject: [PATCH 28/46] devbox v1alpha2 --- controllers/devbox/Makefile | 4 +- controllers/devbox/PROJECT | 18 ++++ .../devbox/api/v1alpha2/devbox_types.go | 12 ++- .../api/v1alpha2/devboxrelease_types.go | 64 +++++++++++++ .../devbox/api/v1alpha2/groupversion_info.go | 4 +- .../api/v1alpha2/zz_generated.deepcopy.go | 89 +++++++++++++++++++ controllers/devbox/cmd/main.go | 9 +- .../crd/bases/devbox.sealos.io_devboxes.yaml | 16 ++-- .../devbox.sealos.io_devboxreleases.yaml | 54 +++++++++++ .../devbox/config/crd/kustomization.yaml | 1 + .../config/rbac/devboxrelease_admin_role.yaml | 27 ++++++ .../rbac/devboxrelease_editor_role.yaml | 33 +++++++ .../rbac/devboxrelease_viewer_role.yaml | 29 ++++++ .../devbox/config/rbac/kustomization.yaml | 7 ++ controllers/devbox/config/rbac/role.yaml | 3 + .../samples/devbox_v1alpha1_devbox.yaml | 27 ------ .../devbox_v1alpha1_devboxrelease.yaml | 24 ----- .../devbox_v1alpha1_operationrequest.yaml | 2 +- .../devbox_v1alpha2_devboxrelease.yaml | 9 ++ .../devbox/config/samples/kustomization.yaml | 4 +- .../samples/test/devbox_v1alpha1_devbox.yaml | 29 ------ .../samples/test/devbox_v1alpha1_runtime.yaml | 34 ------- .../devbox/deploy/manifests/deploy.yaml.tmpl | 6 +- .../internal/controller/devbox_controller.go | 4 +- .../controller/devboxrelease_controller.go | 63 +++++++++++++ .../devboxrelease_controller_test.go | 84 +++++++++++++++++ .../internal/controller/helper/devbox.go | 34 ------- .../controller/state_change_handler.go | 5 +- 28 files changed, 518 insertions(+), 177 deletions(-) create mode 100644 controllers/devbox/api/v1alpha2/devboxrelease_types.go create mode 100644 controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml create mode 100644 controllers/devbox/config/rbac/devboxrelease_admin_role.yaml create mode 100644 controllers/devbox/config/rbac/devboxrelease_editor_role.yaml create mode 100644 controllers/devbox/config/rbac/devboxrelease_viewer_role.yaml delete mode 100644 controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml delete mode 100644 controllers/devbox/config/samples/devbox_v1alpha1_devboxrelease.yaml create mode 100644 controllers/devbox/config/samples/devbox_v1alpha2_devboxrelease.yaml delete mode 100644 controllers/devbox/config/samples/test/devbox_v1alpha1_devbox.yaml delete mode 100644 controllers/devbox/config/samples/test/devbox_v1alpha1_runtime.yaml create mode 100644 controllers/devbox/internal/controller/devboxrelease_controller.go create mode 100644 controllers/devbox/internal/controller/devboxrelease_controller_test.go diff --git a/controllers/devbox/Makefile b/controllers/devbox/Makefile index 30963cf60ee3..7fcfb46f7e46 100644 --- a/controllers/devbox/Makefile +++ b/controllers/devbox/Makefile @@ -4,7 +4,7 @@ IMG ?= ghcr.io/labring/sealos-devbox-controller:latest TARGETARCH ?= amd64 # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.24.1 +ENVTEST_K8S_VERSION = 1.28.0 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -127,7 +127,7 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions KUSTOMIZE_VERSION ?= v5.3.0 -CONTROLLER_TOOLS_VERSION ?= v0.16.0 +CONTROLLER_TOOLS_VERSION ?= v0.16.1 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize diff --git a/controllers/devbox/PROJECT b/controllers/devbox/PROJECT index 23f8331beea7..5cbde8b152a4 100644 --- a/controllers/devbox/PROJECT +++ b/controllers/devbox/PROJECT @@ -17,4 +17,22 @@ resources: kind: Devbox path: github.com/labring/sealos/controllers/devbox/api/v1alpha2 version: v1alpha2 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: sealos.io + group: devbox + kind: Devboxreleases + path: github.com/labring/sealos/controllers/devbox/api/v1alpha2 + version: v1alpha2 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: sealos.io + group: devbox + kind: Devboxrelease + path: github.com/labring/sealos/controllers/devbox/api/v1alpha2 + version: v1alpha2 version: "3" diff --git a/controllers/devbox/api/v1alpha2/devbox_types.go b/controllers/devbox/api/v1alpha2/devbox_types.go index db94601a4fe4..2d4573806147 100644 --- a/controllers/devbox/api/v1alpha2/devbox_types.go +++ b/controllers/devbox/api/v1alpha2/devbox_types.go @@ -53,9 +53,9 @@ const ( DevboxStateRunning DevboxState = "Running" // DevboxStatePending means the Devbox is pending DevboxStatePending DevboxState = "Pending" - // DevboxStatePaused means the Devbox is paused, pod will be released + // DevboxStatePaused means the Devbox is paused, pod will be released but content lv and nodeport service will be retained DevboxStatePaused DevboxState = "Paused" - // DevboxStateStopped means the Devbox is stopped, pod and content lv will be released + // DevboxStateStopped means the Devbox is stopped, pod and content lv will be released but nodeport service will be retained DevboxStateStopped DevboxState = "Stopped" // DevboxStateShutdown means the devbox is shutdown, pod, content lv and nodeport service will be released DevboxStateShutdown DevboxState = "Shutdown" @@ -79,7 +79,7 @@ type NetworkSpec struct { // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=NodePort;Tailnet Type NetworkType `json:"type"` - // // +kubebuilder:validation:Optional + // +kubebuilder:validation:Optional ExtraPorts []corev1.ContainerPort `json:"extraPorts,omitempty"` } @@ -127,7 +127,7 @@ type Config struct { // DevboxSpec defines the desired state of Devbox type DevboxSpec struct { // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum=Running;Stopped;Shutdown + // +kubebuilder:validation:Enum=Running;Paused;Stopped;Shutdown // +kubebuilder:default=Running State DevboxState `json:"state"` // +kubebuilder:validation:Required @@ -192,6 +192,10 @@ const ( DevboxPhaseRunning DevboxPhase = "Running" // DevboxPhasePending means Devbox is run but not run success DevboxPhasePending DevboxPhase = "Pending" + // DevboxPhasePaused means Devbox is paused and paused success + DevboxPhasePaused DevboxPhase = "Paused" + // DevboxPhasePausing means Devbox is pausing + DevboxPhasePausing DevboxPhase = "Pausing" //DevboxPhaseStopped means Devbox is stop and stopped success DevboxPhaseStopped DevboxPhase = "Stopped" //DevboxPhaseStopping means Devbox is stopping diff --git a/controllers/devbox/api/v1alpha2/devboxrelease_types.go b/controllers/devbox/api/v1alpha2/devboxrelease_types.go new file mode 100644 index 000000000000..20b98fdc006a --- /dev/null +++ b/controllers/devbox/api/v1alpha2/devboxrelease_types.go @@ -0,0 +1,64 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// DevboxreleaseSpec defines the desired state of Devboxrelease. +type DevboxreleaseSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Foo is an example field of Devboxrelease. Edit devboxrelease_types.go to remove/update + Foo string `json:"foo,omitempty"` +} + +// DevboxreleaseStatus defines the observed state of Devboxrelease. +type DevboxreleaseStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// Devboxrelease is the Schema for the devboxreleases API. +type Devboxrelease struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DevboxreleaseSpec `json:"spec,omitempty"` + Status DevboxreleaseStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// DevboxreleaseList contains a list of Devboxrelease. +type DevboxreleaseList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Devboxrelease `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Devboxrelease{}, &DevboxreleaseList{}) +} diff --git a/controllers/devbox/api/v1alpha2/groupversion_info.go b/controllers/devbox/api/v1alpha2/groupversion_info.go index 8cc11552321d..c0297899c646 100644 --- a/controllers/devbox/api/v1alpha2/groupversion_info.go +++ b/controllers/devbox/api/v1alpha2/groupversion_info.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1alpha1 contains API Schema definitions for the devbox v1alpha1 API group +// Package v1alpha2 contains API Schema definitions for the devbox v1alpha2 API group // +kubebuilder:object:generate=true // +groupName=devbox.sealos.io package v1alpha2 @@ -26,7 +26,7 @@ import ( var ( // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "devbox.sealos.io", Version: "v1alpha1"} + GroupVersion = schema.GroupVersion{Group: "devbox.sealos.io", Version: "v1alpha2"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go index 769dc5365290..90c9b0d2aebd 100644 --- a/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go +++ b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go @@ -290,6 +290,95 @@ func (in *DevboxStatus) DeepCopy() *DevboxStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Devboxrelease) DeepCopyInto(out *Devboxrelease) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Devboxrelease. +func (in *Devboxrelease) DeepCopy() *Devboxrelease { + if in == nil { + return nil + } + out := new(Devboxrelease) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Devboxrelease) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevboxreleaseList) DeepCopyInto(out *DevboxreleaseList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Devboxrelease, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxreleaseList. +func (in *DevboxreleaseList) DeepCopy() *DevboxreleaseList { + if in == nil { + return nil + } + out := new(DevboxreleaseList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DevboxreleaseList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevboxreleaseSpec) DeepCopyInto(out *DevboxreleaseSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxreleaseSpec. +func (in *DevboxreleaseSpec) DeepCopy() *DevboxreleaseSpec { + if in == nil { + return nil + } + out := new(DevboxreleaseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevboxreleaseStatus) DeepCopyInto(out *DevboxreleaseStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxreleaseStatus. +func (in *DevboxreleaseStatus) DeepCopy() *DevboxreleaseStatus { + if in == nil { + return nil + } + out := new(DevboxreleaseStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) { *out = *in diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 5303498fae4a..5d17ef460566 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -63,7 +63,6 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(devboxv1alpha2.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -313,6 +312,14 @@ func main() { stateChangeHandler.Handle(context.Background(), event) }) defer watcher.Stop() + + if err = (&controller.DevboxreleaseReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Devboxrelease") + os.Exit(1) + } // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index 7af59073cb17..811c8532a06a 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.0 + controller-gen.kubebuilder.io/version: v0.16.1 name: devboxes.devbox.sealos.io spec: group: devbox.sealos.io @@ -1030,9 +1030,7 @@ spec: format: int32 type: integer protocol: - allOf: - - default: TCP - - default: TCP + default: TCP description: |- The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". Default is TCP. @@ -1220,9 +1218,7 @@ spec: referred to by services. type: string protocol: - allOf: - - default: TCP - - default: TCP + default: TCP description: |- Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". @@ -3109,7 +3105,6 @@ spec: network: properties: extraPorts: - description: // +kubebuilder:validation:Optional items: description: ContainerPort represents a network port in a single container. @@ -3138,9 +3133,7 @@ spec: referred to by services. type: string protocol: - allOf: - - default: TCP - - default: TCP + default: TCP description: |- Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". @@ -3179,6 +3172,7 @@ spec: default: Running enum: - Running + - Paused - Stopped - Shutdown type: string diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml new file mode 100644 index 000000000000..2db1520a7d7f --- /dev/null +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml @@ -0,0 +1,54 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: devboxreleases.devbox.sealos.io +spec: + group: devbox.sealos.io + names: + kind: Devboxrelease + listKind: DevboxreleaseList + plural: devboxreleases + singular: devboxrelease + scope: Namespaced + versions: + - name: v1alpha2 + schema: + openAPIV3Schema: + description: Devboxrelease is the Schema for the devboxreleases 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 + 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 + type: string + metadata: + type: object + spec: + description: DevboxreleaseSpec defines the desired state of Devboxrelease. + properties: + foo: + description: Foo is an example field of Devboxrelease. Edit devboxrelease_types.go + to remove/update + type: string + type: object + status: + description: DevboxreleaseStatus defines the observed state of Devboxrelease. + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/controllers/devbox/config/crd/kustomization.yaml b/controllers/devbox/config/crd/kustomization.yaml index 26fcd276a675..c44033a8ecda 100644 --- a/controllers/devbox/config/crd/kustomization.yaml +++ b/controllers/devbox/config/crd/kustomization.yaml @@ -17,6 +17,7 @@ # It should be run by config/default resources: - bases/devbox.sealos.io_devboxes.yaml +- bases/devbox.sealos.io_devboxreleases.yaml # +kubebuilder:scaffold:crdkustomizeresource patches: diff --git a/controllers/devbox/config/rbac/devboxrelease_admin_role.yaml b/controllers/devbox/config/rbac/devboxrelease_admin_role.yaml new file mode 100644 index 000000000000..dbb4efdb5999 --- /dev/null +++ b/controllers/devbox/config/rbac/devboxrelease_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project devbox itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over devbox.sealos.io. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: devbox + app.kubernetes.io/managed-by: kustomize + name: devboxrelease-admin-role +rules: +- apiGroups: + - devbox.sealos.io + resources: + - devboxreleases + verbs: + - '*' +- apiGroups: + - devbox.sealos.io + resources: + - devboxreleases/status + verbs: + - get diff --git a/controllers/devbox/config/rbac/devboxrelease_editor_role.yaml b/controllers/devbox/config/rbac/devboxrelease_editor_role.yaml new file mode 100644 index 000000000000..8ee7479bd903 --- /dev/null +++ b/controllers/devbox/config/rbac/devboxrelease_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project devbox itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the devbox.sealos.io. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: devbox + app.kubernetes.io/managed-by: kustomize + name: devboxrelease-editor-role +rules: +- apiGroups: + - devbox.sealos.io + resources: + - devboxreleases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - devbox.sealos.io + resources: + - devboxreleases/status + verbs: + - get diff --git a/controllers/devbox/config/rbac/devboxrelease_viewer_role.yaml b/controllers/devbox/config/rbac/devboxrelease_viewer_role.yaml new file mode 100644 index 000000000000..3d861f07caee --- /dev/null +++ b/controllers/devbox/config/rbac/devboxrelease_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project devbox itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to devbox.sealos.io resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: devbox + app.kubernetes.io/managed-by: kustomize + name: devboxrelease-viewer-role +rules: +- apiGroups: + - devbox.sealos.io + resources: + - devboxreleases + verbs: + - get + - list + - watch +- apiGroups: + - devbox.sealos.io + resources: + - devboxreleases/status + verbs: + - get diff --git a/controllers/devbox/config/rbac/kustomization.yaml b/controllers/devbox/config/rbac/kustomization.yaml index d092c10c0104..23645e3fb069 100644 --- a/controllers/devbox/config/rbac/kustomization.yaml +++ b/controllers/devbox/config/rbac/kustomization.yaml @@ -38,3 +38,10 @@ resources: # if you do not want those helpers be installed with your Project. - devbox_editor_role.yaml - devbox_viewer_role.yaml +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the {{ .ProjectName }} itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- devboxrelease_admin_role.yaml +- devboxrelease_editor_role.yaml +- devboxrelease_viewer_role.yaml diff --git a/controllers/devbox/config/rbac/role.yaml b/controllers/devbox/config/rbac/role.yaml index 918201b2f065..97afb6106c29 100644 --- a/controllers/devbox/config/rbac/role.yaml +++ b/controllers/devbox/config/rbac/role.yaml @@ -39,6 +39,7 @@ rules: - devbox.sealos.io resources: - devboxes + - devboxreleases - runtimeclasses - runtimes verbs: @@ -53,12 +54,14 @@ rules: - devbox.sealos.io resources: - devboxes/finalizers + - devboxreleases/finalizers verbs: - update - apiGroups: - devbox.sealos.io resources: - devboxes/status + - devboxreleases/status verbs: - get - patch diff --git a/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml b/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml deleted file mode 100644 index ca0938eb745c..000000000000 --- a/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: devbox.sealos.io/v1alpha1 -kind: Devbox -metadata: - name: devbox-sample -spec: - state: Running - image: busybox:latest - config: {} - resource: - cpu: 2 - memory: 4000Mi - network: - type: NodePort diff --git a/controllers/devbox/config/samples/devbox_v1alpha1_devboxrelease.yaml b/controllers/devbox/config/samples/devbox_v1alpha1_devboxrelease.yaml deleted file mode 100644 index 15afbb39bffd..000000000000 --- a/controllers/devbox/config/samples/devbox_v1alpha1_devboxrelease.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: devbox.sealos.io/v1alpha1 -kind: DevBoxRelease -metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: devboxrelease-sample -spec: - devboxName: devbox-sample - newTag: v1.0.0 diff --git a/controllers/devbox/config/samples/devbox_v1alpha1_operationrequest.yaml b/controllers/devbox/config/samples/devbox_v1alpha1_operationrequest.yaml index dd73c69f5aaf..9702fec947b3 100644 --- a/controllers/devbox/config/samples/devbox_v1alpha1_operationrequest.yaml +++ b/controllers/devbox/config/samples/devbox_v1alpha1_operationrequest.yaml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -apiVersion: devbox.sealos.io/v1alpha1 +apiVersion: devbox.sealos.io/v1alpha2 kind: OperationRequest metadata: labels: diff --git a/controllers/devbox/config/samples/devbox_v1alpha2_devboxrelease.yaml b/controllers/devbox/config/samples/devbox_v1alpha2_devboxrelease.yaml new file mode 100644 index 000000000000..39de824d5090 --- /dev/null +++ b/controllers/devbox/config/samples/devbox_v1alpha2_devboxrelease.yaml @@ -0,0 +1,9 @@ +apiVersion: devbox.sealos.io/v1alpha2 +kind: Devboxrelease +metadata: + labels: + app.kubernetes.io/name: devbox + app.kubernetes.io/managed-by: kustomize + name: devboxrelease-sample +spec: + # TODO(user): Add fields here diff --git a/controllers/devbox/config/samples/kustomization.yaml b/controllers/devbox/config/samples/kustomization.yaml index 26ee8c0257f2..4e87de83e283 100644 --- a/controllers/devbox/config/samples/kustomization.yaml +++ b/controllers/devbox/config/samples/kustomization.yaml @@ -14,5 +14,7 @@ ## Append samples of your project ## resources: -- devbox_v1alpha1_devbox.yaml +- devbox_v1alpha2_devbox.yaml +- devbox_v1alpha2_devboxreleases.yaml +- devbox_v1alpha2_devboxrelease.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/controllers/devbox/config/samples/test/devbox_v1alpha1_devbox.yaml b/controllers/devbox/config/samples/test/devbox_v1alpha1_devbox.yaml deleted file mode 100644 index 7a88714a0529..000000000000 --- a/controllers/devbox/config/samples/test/devbox_v1alpha1_devbox.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: devbox.sealos.io/v1alpha1 -kind: Devbox -metadata: - name: devbox-sample-1 -spec: - squash: false - network: - type: NodePort - runtimeClassName: devbox-runtime - resource: - cpu: 2000m - memory: 4096Mi - image: ghcr.io/labring-actions/devbox/go-1.23.0:13aacd8 - config: - workingDir: /home/devbox/project diff --git a/controllers/devbox/config/samples/test/devbox_v1alpha1_runtime.yaml b/controllers/devbox/config/samples/test/devbox_v1alpha1_runtime.yaml deleted file mode 100644 index fa3775f1637e..000000000000 --- a/controllers/devbox/config/samples/test/devbox_v1alpha1_runtime.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: devbox.sealos.io/v1alpha1 -kind: Runtime -metadata: - name: go-1-22-5 - namespace: devbox-system -spec: - classRef: go - version: go1.22.5 - description: go1.22.5 - config: - image: ubuntu - workingDir: /home/sealos/project - releaseCommand: - - /bin/bash - - -c - releaseArgs: - - /home/sealos/project/entrypoint.sh - category: - - ubuntu - - go diff --git a/controllers/devbox/deploy/manifests/deploy.yaml.tmpl b/controllers/devbox/deploy/manifests/deploy.yaml.tmpl index 6b5b8a8e349a..c428c30114d1 100644 --- a/controllers/devbox/deploy/manifests/deploy.yaml.tmpl +++ b/controllers/devbox/deploy/manifests/deploy.yaml.tmpl @@ -49,7 +49,7 @@ spec: - jsonPath: .status.phase name: Phase type: string - name: v1alpha1 + name: v1alpha2 schema: openAPIV3Schema: description: Devbox is the Schema for the devboxes API @@ -3099,7 +3099,7 @@ spec: - jsonPath: .status.originalImage name: OriginalImage type: string - name: v1alpha1 + name: v1alpha2 schema: openAPIV3Schema: description: DevBoxRelease is the Schema for the devboxreleases API @@ -3164,7 +3164,7 @@ spec: singular: operationrequest scope: Namespaced versions: - - name: v1alpha1 + - name: v1alpha2 schema: openAPIV3Schema: description: OperationRequest is the Schema for the operationrequests API diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 216b9b0a0f4d..a756a11e2bef 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -332,7 +332,7 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha2.D r.Recorder.Eventf(devbox, corev1.EventTypeWarning, "More than one pod found", "More than one pod found") return fmt.Errorf("more than one pod found") } - case devboxv1alpha2.DevboxStateStopped, devboxv1alpha2.DevboxStateShutdown: + case devboxv1alpha2.DevboxStatePaused, devboxv1alpha2.DevboxStateStopped, devboxv1alpha2.DevboxStateShutdown: if len(podList.Items) > 0 { for _, pod := range podList.Items { r.deletePod(ctx, &pod) @@ -387,7 +387,7 @@ func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alph NodePort: int32(0), } return r.Status().Update(ctx, devbox) - case devboxv1alpha2.DevboxStateRunning, devboxv1alpha2.DevboxStateStopped: + case devboxv1alpha2.DevboxStateRunning, devboxv1alpha2.DevboxStatePaused, devboxv1alpha2.DevboxStateStopped: if _, err := controllerutil.CreateOrUpdate(ctx, r.Client, service, func() error { // only update some specific fields service.Spec.Selector = expectServiceSpec.Selector diff --git a/controllers/devbox/internal/controller/devboxrelease_controller.go b/controllers/devbox/internal/controller/devboxrelease_controller.go new file mode 100644 index 000000000000..d2fe5d759248 --- /dev/null +++ b/controllers/devbox/internal/controller/devboxrelease_controller.go @@ -0,0 +1,63 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" +) + +// DevboxreleaseReconciler reconciles a Devboxrelease object +type DevboxreleaseReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Devboxrelease object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile +func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *DevboxreleaseReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&devboxv1alpha2.Devboxrelease{}). + Named("devboxrelease"). + Complete(r) +} diff --git a/controllers/devbox/internal/controller/devboxrelease_controller_test.go b/controllers/devbox/internal/controller/devboxrelease_controller_test.go new file mode 100644 index 000000000000..01f53ed1e24b --- /dev/null +++ b/controllers/devbox/internal/controller/devboxrelease_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" +) + +var _ = Describe("Devboxrelease Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + devboxrelease := &devboxv1alpha2.Devboxrelease{} + + BeforeEach(func() { + By("creating the custom resource for the Kind Devboxrelease") + err := k8sClient.Get(ctx, typeNamespacedName, devboxrelease) + if err != nil && errors.IsNotFound(err) { + resource := &devboxv1alpha2.Devboxrelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &devboxv1alpha2.Devboxrelease{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance Devboxrelease") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &DevboxreleaseReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/controllers/devbox/internal/controller/helper/devbox.go b/controllers/devbox/internal/controller/helper/devbox.go index d8283e369d55..127fe98250fd 100644 --- a/controllers/devbox/internal/controller/helper/devbox.go +++ b/controllers/devbox/internal/controller/helper/devbox.go @@ -125,40 +125,6 @@ func GeneratePodAnnotations(devbox *devboxv1alpha2.Devbox) map[string]string { return annotations } -func GenerateDevboxPhase(devbox *devboxv1alpha2.Devbox, podList corev1.PodList) devboxv1alpha2.DevboxPhase { - if len(podList.Items) > 1 { - return devboxv1alpha2.DevboxPhaseError - } - switch devbox.Spec.State { - case devboxv1alpha2.DevboxStateRunning: - if len(podList.Items) == 0 { - return devboxv1alpha2.DevboxPhasePending - } - switch podList.Items[0].Status.Phase { - case corev1.PodFailed, corev1.PodSucceeded: - return devboxv1alpha2.DevboxPhaseStopped - case corev1.PodPending: - return devboxv1alpha2.DevboxPhasePending - case corev1.PodRunning: - if podList.Items[0].Status.ContainerStatuses[0].Ready && podList.Items[0].Status.ContainerStatuses[0].ContainerID != "" { - return devboxv1alpha2.DevboxPhaseRunning - } - return devboxv1alpha2.DevboxPhasePending - } - case devboxv1alpha2.DevboxStateStopped: - if len(podList.Items) == 0 { - return devboxv1alpha2.DevboxPhaseStopped - } - return devboxv1alpha2.DevboxPhaseStopping - case devboxv1alpha2.DevboxStateShutdown: - if len(podList.Items) == 0 { - return devboxv1alpha2.DevboxPhaseShutdown - } - return devboxv1alpha2.DevboxPhaseShutting - } - return devboxv1alpha2.DevboxPhaseUnknown -} - func GenerateSSHKeyPair() ([]byte, []byte, error) { pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 03b1d6de84e0..9a0622f49bdd 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -61,8 +61,9 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er return fmt.Errorf("shutdown state is not allowed to be changed to stopped state") } - // Handle state transitions that require commit - needsCommit := (currentState == devboxv1alpha2.DevboxStateRunning || currentState == devboxv1alpha2.DevboxStateStopped) && targetState == devboxv1alpha2.DevboxStateShutdown + // Handle state transitions that require commit, only running and paused devbox can be shutdown or stopped + needsCommit := (targetState == devboxv1alpha2.DevboxStateShutdown || targetState == devboxv1alpha2.DevboxStateStopped) && + (currentState == devboxv1alpha2.DevboxStateRunning || currentState == devboxv1alpha2.DevboxStatePaused) if needsCommit { // Check if commit is already in progress to prevent duplicate requests From 7ff4b4c310d278764483b17c0b549721e7b78ce7 Mon Sep 17 00:00:00 2001 From: yy Date: Fri, 22 Aug 2025 14:32:07 +0800 Subject: [PATCH 29/46] devbox release define --- .../api/v1alpha2/devboxrelease_types.go | 30 ++++++++++++------- .../devbox.sealos.io_devboxreleases.yaml | 20 ++++++++++--- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/controllers/devbox/api/v1alpha2/devboxrelease_types.go b/controllers/devbox/api/v1alpha2/devboxrelease_types.go index 20b98fdc006a..23851aa4b67c 100644 --- a/controllers/devbox/api/v1alpha2/devboxrelease_types.go +++ b/controllers/devbox/api/v1alpha2/devboxrelease_types.go @@ -20,22 +20,30 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - // DevboxreleaseSpec defines the desired state of Devboxrelease. type DevboxreleaseSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Foo is an example field of Devboxrelease. Edit devboxrelease_types.go to remove/update - Foo string `json:"foo,omitempty"` + // +kubebuilder:validation:Required + DevboxName string `json:"devboxName"` + // +kubebuilder:validation:Required + Version string `json:"version"` + // +kubebuilder:validation:Optional + Notes string `json:"notes,omitempty"` } -// DevboxreleaseStatus defines the observed state of Devboxrelease. +type DevboxReleasePhase string + +const ( + // DevboxReleasePhaseSuccess means the Devbox has been released + DevboxReleasePhaseSuccess DevboxReleasePhase = "Success" + // DevboxReleasePhasePending means the Devbox has not been released + DevboxReleasePhasePending DevboxReleasePhase = "Pending" + // DevboxReleasePhaseFailed means the Devbox has not been released + DevboxReleasePhaseFailed DevboxReleasePhase = "Failed" +) + type DevboxreleaseStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + Phase DevboxReleasePhase `json:"phase"` + OriginalDevboxState DevboxState `json:"originalDevboxState"` } // +kubebuilder:object:root=true diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml index 2db1520a7d7f..0e6afc6b6a5a 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml @@ -39,13 +39,25 @@ spec: spec: description: DevboxreleaseSpec defines the desired state of Devboxrelease. properties: - foo: - description: Foo is an example field of Devboxrelease. Edit devboxrelease_types.go - to remove/update + devboxName: type: string + notes: + type: string + version: + type: string + required: + - devboxName + - version type: object status: - description: DevboxreleaseStatus defines the observed state of Devboxrelease. + properties: + originalDevboxState: + type: string + phase: + type: string + required: + - originalDevboxState + - phase type: object type: object served: true From 9f1e1678aa9ffccc6868cfdd31287b4ca22d2d34 Mon Sep 17 00:00:00 2001 From: yy Date: Tue, 26 Aug 2025 15:21:18 +0800 Subject: [PATCH 30/46] devbox release impl, retag refactor --- controllers/devbox/PROJECT | 2 +- .../api/v1alpha2/devboxrelease_types.go | 28 +-- .../api/v1alpha2/zz_generated.deepcopy.go | 178 +++++++++--------- controllers/devbox/cmd/main.go | 13 +- .../devbox.sealos.io_devboxreleases.yaml | 8 +- controllers/devbox/go.mod | 22 ++- controllers/devbox/go.sum | 27 +++ .../controller/devboxrelease_controller.go | 83 ++++++-- .../devboxrelease_controller_test.go | 6 +- .../controller/utils/registry/client.go | 95 ++++++---- .../controller/utils/registry/client_test.go | 22 +-- controllers/go.work.sum | 20 +- 12 files changed, 313 insertions(+), 191 deletions(-) diff --git a/controllers/devbox/PROJECT b/controllers/devbox/PROJECT index 5cbde8b152a4..1e9e3bb1c399 100644 --- a/controllers/devbox/PROJECT +++ b/controllers/devbox/PROJECT @@ -32,7 +32,7 @@ resources: controller: true domain: sealos.io group: devbox - kind: Devboxrelease + kind: DevboxRelease path: github.com/labring/sealos/controllers/devbox/api/v1alpha2 version: v1alpha2 version: "3" diff --git a/controllers/devbox/api/v1alpha2/devboxrelease_types.go b/controllers/devbox/api/v1alpha2/devboxrelease_types.go index 23851aa4b67c..caef50e5338c 100644 --- a/controllers/devbox/api/v1alpha2/devboxrelease_types.go +++ b/controllers/devbox/api/v1alpha2/devboxrelease_types.go @@ -20,8 +20,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// DevboxreleaseSpec defines the desired state of Devboxrelease. -type DevboxreleaseSpec struct { +// DevboxReleaseSpec defines the desired state of Devboxrelease. +type DevboxReleaseSpec struct { // +kubebuilder:validation:Required DevboxName string `json:"devboxName"` // +kubebuilder:validation:Required @@ -41,32 +41,34 @@ const ( DevboxReleasePhaseFailed DevboxReleasePhase = "Failed" ) -type DevboxreleaseStatus struct { - Phase DevboxReleasePhase `json:"phase"` - OriginalDevboxState DevboxState `json:"originalDevboxState"` +type DevboxReleaseStatus struct { + Phase DevboxReleasePhase `json:"phase,omitempty"` + OriginalDevboxState DevboxState `json:"originalDevboxState,omitempty"` + SourceImage string `json:"sourceImage,omitempty"` + TargetImage string `json:"targetImage,omitempty"` } // +kubebuilder:object:root=true // +kubebuilder:subresource:status -// Devboxrelease is the Schema for the devboxreleases API. -type Devboxrelease struct { +// DevboxRelease is the Schema for the devboxreleases API. +type DevboxRelease struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec DevboxreleaseSpec `json:"spec,omitempty"` - Status DevboxreleaseStatus `json:"status,omitempty"` + Spec DevboxReleaseSpec `json:"spec,omitempty"` + Status DevboxReleaseStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true -// DevboxreleaseList contains a list of Devboxrelease. -type DevboxreleaseList struct { +// DevboxReleaseList contains a list of DevboxRelease. +type DevboxReleaseList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []Devboxrelease `json:"items"` + Items []DevboxRelease `json:"items"` } func init() { - SchemeBuilder.Register(&Devboxrelease{}, &DevboxreleaseList{}) + SchemeBuilder.Register(&DevboxRelease{}, &DevboxReleaseList{}) } diff --git a/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go index 90c9b0d2aebd..b9ebc76bae4e 100644 --- a/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go +++ b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go @@ -215,6 +215,95 @@ func (in *DevboxList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevboxRelease) DeepCopyInto(out *DevboxRelease) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxRelease. +func (in *DevboxRelease) DeepCopy() *DevboxRelease { + if in == nil { + return nil + } + out := new(DevboxRelease) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DevboxRelease) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevboxReleaseList) DeepCopyInto(out *DevboxReleaseList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DevboxRelease, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxReleaseList. +func (in *DevboxReleaseList) DeepCopy() *DevboxReleaseList { + if in == nil { + return nil + } + out := new(DevboxReleaseList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DevboxReleaseList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevboxReleaseSpec) DeepCopyInto(out *DevboxReleaseSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxReleaseSpec. +func (in *DevboxReleaseSpec) DeepCopy() *DevboxReleaseSpec { + if in == nil { + return nil + } + out := new(DevboxReleaseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevboxReleaseStatus) DeepCopyInto(out *DevboxReleaseStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxReleaseStatus. +func (in *DevboxReleaseStatus) DeepCopy() *DevboxReleaseStatus { + if in == nil { + return nil + } + out := new(DevboxReleaseStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DevboxSpec) DeepCopyInto(out *DevboxSpec) { *out = *in @@ -290,95 +379,6 @@ func (in *DevboxStatus) DeepCopy() *DevboxStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Devboxrelease) DeepCopyInto(out *Devboxrelease) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Devboxrelease. -func (in *Devboxrelease) DeepCopy() *Devboxrelease { - if in == nil { - return nil - } - out := new(Devboxrelease) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Devboxrelease) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevboxreleaseList) DeepCopyInto(out *DevboxreleaseList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Devboxrelease, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxreleaseList. -func (in *DevboxreleaseList) DeepCopy() *DevboxreleaseList { - if in == nil { - return nil - } - out := new(DevboxreleaseList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DevboxreleaseList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevboxreleaseSpec) DeepCopyInto(out *DevboxreleaseSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxreleaseSpec. -func (in *DevboxreleaseSpec) DeepCopy() *DevboxreleaseSpec { - if in == nil { - return nil - } - out := new(DevboxreleaseSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevboxreleaseStatus) DeepCopyInto(out *DevboxreleaseStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxreleaseStatus. -func (in *DevboxreleaseStatus) DeepCopy() *DevboxreleaseStatus { - if in == nil { - return nil - } - out := new(DevboxreleaseStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) { *out = *in diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 5d17ef460566..ed28e095db25 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -51,6 +51,7 @@ import ( "github.com/labring/sealos/controllers/devbox/internal/controller" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/nodes" + "github.com/labring/sealos/controllers/devbox/internal/controller/utils/registry" utilresource "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" "github.com/labring/sealos/controllers/devbox/internal/stat" // +kubebuilder:scaffold:imports @@ -298,10 +299,7 @@ func main() { Logger: ctrl.Log.WithName("state-change-handler"), } - // 添加调试日志 - setupLog.Info("StateChangeHandler initialized", - "nodeName", nodes.GetNodeName(), - "registryAddr", registryAddr) + setupLog.Info("StateChangeHandler initialized", "nodeName", nodes.GetNodeName()) watcher := stateChangeBroadcaster.StartEventWatcher(func(event *corev1.Event) { setupLog.Info("Event received by watcher", @@ -316,6 +314,13 @@ func main() { if err = (&controller.DevboxreleaseReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), + Registry: registry.Registry{ + Host: registryAddr, + BasicAuth: registry.BasicAuth{ + Username: registryUser, + Password: registryPassword, + }, + }, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Devboxrelease") os.Exit(1) diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml index 0e6afc6b6a5a..73b37bfe9650 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml @@ -8,8 +8,8 @@ metadata: spec: group: devbox.sealos.io names: - kind: Devboxrelease - listKind: DevboxreleaseList + kind: DevboxRelease + listKind: DevboxReleaseList plural: devboxreleases singular: devboxrelease scope: Namespaced @@ -17,7 +17,7 @@ spec: - name: v1alpha2 schema: openAPIV3Schema: - description: Devboxrelease is the Schema for the devboxreleases API. + description: DevboxRelease is the Schema for the devboxreleases API. properties: apiVersion: description: |- @@ -37,7 +37,7 @@ spec: metadata: type: object spec: - description: DevboxreleaseSpec defines the desired state of Devboxrelease. + description: DevboxReleaseSpec defines the desired state of Devboxrelease. properties: devboxName: type: string diff --git a/controllers/devbox/go.mod b/controllers/devbox/go.mod index 371ab733d6f8..8990d8222891 100644 --- a/controllers/devbox/go.mod +++ b/controllers/devbox/go.mod @@ -6,7 +6,7 @@ require ( github.com/containerd/containerd/v2 v2.1.4 github.com/containerd/errdefs v1.0.0 github.com/containerd/nerdctl/v2 v2.1.3 - github.com/go-logr/logr v1.4.2 + github.com/go-logr/logr v1.4.3 github.com/google/uuid v1.6.0 github.com/onsi/ginkgo/v2 v2.23.4 github.com/onsi/gomega v1.37.0 @@ -70,9 +70,10 @@ require ( github.com/djherbis/times v1.6.0 // indirect github.com/docker/cli v28.3.2+incompatible // indirect github.com/docker/docker v28.3.2+incompatible // indirect - github.com/docker/docker-credential-helpers v0.8.2 // indirect + github.com/docker/docker-credential-helpers v0.9.3 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fahedouch/go-logrotate v0.3.0 // indirect @@ -96,6 +97,7 @@ require ( github.com/google/cel-go v0.22.0 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/go-containerregistry v0.20.6 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect @@ -144,6 +146,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/regclient/regclient v0.9.0 // indirect github.com/rootless-containers/bypass4netns v0.4.2 // indirect github.com/rootless-containers/rootlesskit/v2 v2.3.5 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect @@ -156,20 +159,21 @@ require ( github.com/stoewer/go-strcase v1.3.0 // indirect github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/tinylib/msgp v1.3.0 // indirect - github.com/vbatts/tar-split v0.11.6 // indirect + github.com/ulikunitz/xz v0.5.12 // indirect + github.com/vbatts/tar-split v0.12.1 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yuchanns/srslog v1.1.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -177,7 +181,7 @@ require ( golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/mod v0.25.0 // indirect golang.org/x/net v0.42.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.16.0 // indirect golang.org/x/sys v0.34.0 // indirect golang.org/x/term v0.33.0 // indirect diff --git a/controllers/devbox/go.sum b/controllers/devbox/go.sum index 424ef6026269..ff59b9b576f6 100644 --- a/controllers/devbox/go.sum +++ b/controllers/devbox/go.sum @@ -105,10 +105,14 @@ github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5Xym github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= +github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= +github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 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-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -134,6 +138,8 @@ github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JS github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -189,6 +195,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= +github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -333,6 +341,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/regclient/regclient v0.9.0 h1:c3hNJZvtv8lMqhP0jGCa4d9j2n4688VCfhCWddGfWfk= +github.com/regclient/regclient v0.9.0/go.mod h1:Adv7tukwdX+oDTszfILrjerGk55Pg2nKlbshj94U3rg= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rootless-containers/bypass4netns v0.4.2 h1:JUZcpX7VLRfDkLxBPC6fyNalJGv9MjnjECOilZIvKRc= @@ -376,9 +386,13 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtse github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= +github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= +github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= +github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= @@ -405,20 +419,31 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= @@ -479,6 +504,8 @@ golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/controllers/devbox/internal/controller/devboxrelease_controller.go b/controllers/devbox/internal/controller/devboxrelease_controller.go index d2fe5d759248..e39cf571a8f1 100644 --- a/controllers/devbox/internal/controller/devboxrelease_controller.go +++ b/controllers/devbox/internal/controller/devboxrelease_controller.go @@ -18,18 +18,24 @@ package controller import ( "context" + "errors" + "fmt" + "time" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" + "github.com/labring/sealos/controllers/devbox/internal/controller/utils/registry" ) // DevboxreleaseReconciler reconciles a Devboxrelease object type DevboxreleaseReconciler struct { client.Client + registry.Registry Scheme *runtime.Scheme } @@ -37,27 +43,80 @@ type DevboxreleaseReconciler struct { // +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases/status,verbs=get;update;patch // +kubebuilder:rbac:groups=devbox.sealos.io,resources=devboxreleases/finalizers,verbs=update -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the Devboxrelease object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) + logger := log.FromContext(ctx) + devboxRelease := &devboxv1alpha2.DevboxRelease{} + if err := r.Client.Get(ctx, req.NamespacedName, devboxRelease); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } - // TODO(user): your logic here + if devboxRelease.ObjectMeta.DeletionTimestamp.IsZero() { + if controllerutil.AddFinalizer(devboxRelease, devboxv1alpha2.FinalizerName) { + if err := r.Update(ctx, devboxRelease); err != nil { + return ctrl.Result{}, err + } + } + } else { + if controllerutil.RemoveFinalizer(devboxRelease, devboxv1alpha2.FinalizerName) { + if err := r.Update(ctx, devboxRelease); err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil + } + logger.Info("Reconciling DevBoxRelease", "devbox", devboxRelease.Spec.DevboxName, "version", devboxRelease.Spec.Version, "phase", devboxRelease.Status.Phase) + + if devboxRelease.Status.Phase == "" { + devboxRelease.Status.Phase = devboxv1alpha2.DevboxReleasePhasePending + devbox := &devboxv1alpha2.Devbox{} + if err := r.Get(ctx, req.NamespacedName, devbox); err != nil { + logger.Error(err, "Failed to get devbox", "devbox", devboxRelease.Spec.DevboxName) + return ctrl.Result{}, err + } + devboxRelease.Status.OriginalDevboxState = devbox.Spec.State + devboxRelease.Status.SourceImage = devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage + devboxRelease.Status.TargetImage = fmt.Sprintf("%s/%s/%s:%s", r.Registry.Host, devboxRelease.Namespace, devboxRelease.Spec.DevboxName, devboxRelease.Spec.Version) + if err := r.Status().Update(ctx, devboxRelease); err != nil { + logger.Error(err, "Failed to update status", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + return ctrl.Result{}, err + } + return ctrl.Result{Requeue: true}, nil + } + + if devboxRelease.Status.Phase == devboxv1alpha2.DevboxReleasePhasePending { + logger.Info("Creating release tag", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + err := r.Release(ctx, devboxRelease) + if err != nil && errors.Is(err, registry.ErrorManifestNotFound) { + logger.Info("Manifest not found, retrying", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + return ctrl.Result{RequeueAfter: time.Second * 10}, nil + } else if err != nil { + logger.Error(err, "Failed to create release tag", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + devboxRelease.Status.Phase = devboxv1alpha2.DevboxReleasePhaseFailed + _ = r.Status().Update(ctx, devboxRelease) + return ctrl.Result{}, err + } + logger.Info("Release tag created", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + devboxRelease.Status.Phase = devboxv1alpha2.DevboxReleasePhaseSuccess + if err = r.Status().Update(ctx, devboxRelease); err != nil { + logger.Error(err, "Failed to update status", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + return ctrl.Result{}, err + } + } + logger.Info("Reconciliation complete", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) return ctrl.Result{}, nil } +// todo: implement this function +func (r *DevboxreleaseReconciler) Release(ctx context.Context, devboxRelease *devboxv1alpha2.DevboxRelease) error { + r.Registry.ReTag(devboxRelease.Status.SourceImage, devboxRelease.Status.TargetImage) + return nil +} + // SetupWithManager sets up the controller with the Manager. func (r *DevboxreleaseReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&devboxv1alpha2.Devboxrelease{}). + For(&devboxv1alpha2.DevboxRelease{}). Named("devboxrelease"). Complete(r) } diff --git a/controllers/devbox/internal/controller/devboxrelease_controller_test.go b/controllers/devbox/internal/controller/devboxrelease_controller_test.go index 01f53ed1e24b..1754e49cfeb5 100644 --- a/controllers/devbox/internal/controller/devboxrelease_controller_test.go +++ b/controllers/devbox/internal/controller/devboxrelease_controller_test.go @@ -40,13 +40,13 @@ var _ = Describe("Devboxrelease Controller", func() { Name: resourceName, Namespace: "default", // TODO(user):Modify as needed } - devboxrelease := &devboxv1alpha2.Devboxrelease{} + devboxrelease := &devboxv1alpha2.DevboxRelease{} BeforeEach(func() { By("creating the custom resource for the Kind Devboxrelease") err := k8sClient.Get(ctx, typeNamespacedName, devboxrelease) if err != nil && errors.IsNotFound(err) { - resource := &devboxv1alpha2.Devboxrelease{ + resource := &devboxv1alpha2.DevboxRelease{ ObjectMeta: metav1.ObjectMeta{ Name: resourceName, Namespace: "default", @@ -59,7 +59,7 @@ var _ = Describe("Devboxrelease Controller", func() { AfterEach(func() { // TODO(user): Cleanup logic after each test, like removing the resource instance. - resource := &devboxv1alpha2.Devboxrelease{} + resource := &devboxv1alpha2.DevboxRelease{} err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) diff --git a/controllers/devbox/internal/controller/utils/registry/client.go b/controllers/devbox/internal/controller/utils/registry/client.go index 89f89520dec6..8ecc3ced4cde 100644 --- a/controllers/devbox/internal/controller/utils/registry/client.go +++ b/controllers/devbox/internal/controller/utils/registry/client.go @@ -17,81 +17,104 @@ package registry import ( "bytes" "errors" + "fmt" "io" "net/http" + + "github.com/google/go-containerregistry/pkg/name" ) -type Client struct { +// todo: refactor this struct, add opts for tls or something else +type Opts struct { +} + +type BasicAuth struct { Username string Password string } +type Registry struct { + Host string + BasicAuth BasicAuth +} + var ( ErrorManifestNotFound = errors.New("manifest not found") ) -// TODO: refactor tag image, use go package to do this - -func (t *Client) TagImage(hostName string, imageName string, oldTag string, newTag string) error { - manifest, err := t.pullManifest(t.Username, t.Password, hostName, imageName, oldTag) +// ReTag creates a new tag for an existing image by copying its manifest. +func (c *Registry) ReTag(source, target string) error { + manifest, err := c.pullManifest(source) if err != nil { - return err + return fmt.Errorf("failed to pull manifest for %s: %w", source, err) + } + if err := c.pushManifest(target, manifest); err != nil { + return fmt.Errorf("failed to push manifest for %s: %w", target, err) } - return t.pushManifest(t.Username, t.Password, hostName, imageName, newTag, manifest) + return nil } -func (t *Client) pullManifest(username string, password string, hostName string, imageName string, tag string) ([]byte, error) { - var ( - client = http.DefaultClient - url = "http://" + hostName + "/v2/" + imageName + "/manifests/" + tag - ) +// todo: refactor this function, add opts for tls +func (c *Registry) pullManifest(image string) ([]byte, error) { + ref, err := name.ParseReference(image) + if err != nil { + return nil, fmt.Errorf("failed to parse image: %w", err) + } + + url := fmt.Sprintf("http://%s/v2/%s/manifests/%s", ref.Context().RegistryStr(), ref.Context().RepositoryStr(), ref.Identifier()) + req, err := http.NewRequest("GET", url, nil) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create GET request: %w", err) } - req.SetBasicAuth(username, password) + + req.SetBasicAuth(c.BasicAuth.Username, c.BasicAuth.Password) req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json") - resp, err := client.Do(req) + resp, err := http.DefaultClient.Do(req) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to execute request: %w", err) } + defer resp.Body.Close() - if resp.StatusCode == http.StatusNotFound { + switch resp.StatusCode { + case http.StatusNotFound: return nil, ErrorManifestNotFound + case http.StatusOK: + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %w", err) + } + return body, nil + default: + return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, resp.Status) } +} - if resp.StatusCode != http.StatusOK { - return nil, errors.New(resp.Status) - } - - bodyText, err := io.ReadAll(resp.Body) +// todo: refactor this function, add opts for tls +func (c *Registry) pushManifest(image string, manifest []byte) error { + ref, err := name.ParseReference(image) if err != nil { - return nil, err + return fmt.Errorf("failed to parse image: %w", err) } - return bodyText, nil -} + url := fmt.Sprintf("http://%s/v2/%s/manifests/%s", ref.Context().RegistryStr(), ref.Context().RepositoryStr(), ref.Identifier()) -func (t *Client) pushManifest(username string, password string, hostName string, imageName string, tag string, manifest []byte) error { - var ( - client = http.DefaultClient - url = "http://" + hostName + "/v2/" + imageName + "/manifests/" + tag - ) req, err := http.NewRequest("PUT", url, bytes.NewBuffer(manifest)) if err != nil { - return err + return fmt.Errorf("failed to create PUT request: %w", err) } - req.SetBasicAuth(username, password) - req.Header.Set("Content-type", "application/vnd.docker.distribution.manifest.v2+json") + req.SetBasicAuth(c.BasicAuth.Username, c.BasicAuth.Password) + req.Header.Set("Content-Type", "application/vnd.docker.distribution.manifest.v2+json") - resp, err := client.Do(req) + resp, err := http.DefaultClient.Do(req) if err != nil { - return err + return fmt.Errorf("failed to execute request: %w", err) } + defer resp.Body.Close() if resp.StatusCode != http.StatusCreated { - return errors.New(resp.Status) + return fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, resp.Status) } return nil diff --git a/controllers/devbox/internal/controller/utils/registry/client_test.go b/controllers/devbox/internal/controller/utils/registry/client_test.go index 18dad98e9a5e..b2c185d35b59 100644 --- a/controllers/devbox/internal/controller/utils/registry/client_test.go +++ b/controllers/devbox/internal/controller/utils/registry/client_test.go @@ -22,10 +22,8 @@ func TestClient_TagImage(t1 *testing.T) { Password string } type args struct { - hostName string - imageName string - oldTag string - newTag string + source string + target string } tests := []struct { name string @@ -40,20 +38,20 @@ func TestClient_TagImage(t1 *testing.T) { Password: "passw0rd", }, args: args{ - hostName: "sealos.hub:5000", - imageName: "default/devbox-sample", - oldTag: "2024-08-21-072021", - newTag: "test", + source: "sealos.hub:5000/default/devbox-sample:2024-08-21-072021", + target: "sealos.hub:5000/default/devbox-sample:test", }, }, } for _, tt := range tests { t1.Run(tt.name, func(t1 *testing.T) { - t := &Client{ - Username: tt.fields.Username, - Password: tt.fields.Password, + t := &Registry{ + BasicAuth: BasicAuth{ + Username: tt.fields.Username, + Password: tt.fields.Password, + }, } - if err := t.TagImage(tt.args.hostName, tt.args.imageName, tt.args.oldTag, tt.args.newTag); (err != nil) != tt.wantErr { + if err := t.ReTag(tt.args.source, tt.args.target); (err != nil) != tt.wantErr { t1.Errorf("TagImage() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/controllers/go.work.sum b/controllers/go.work.sum index d467e1fe7aaf..57c30c282f7a 100644 --- a/controllers/go.work.sum +++ b/controllers/go.work.sum @@ -416,6 +416,7 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0 h1:jXIpfcH/VYSE1SYcPzO0n1VVb+sAamiLOgCw45JbOQk= @@ -1712,6 +1713,7 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= @@ -2077,8 +2079,6 @@ github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0 github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= -github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ= -github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -2216,6 +2216,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0q github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -2236,6 +2237,7 @@ github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjI github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= +github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= github.com/data-accelerator/zdfs v0.1.5/go.mod h1:/MyNTsQHHKVLznaRBz+PivhIDwglu+wuXKoZxUNmLKI= github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= github.com/deckarep/golang-set/v2 v2.3.1 h1:vjmkvJt/IV27WXPyYQpAh4bRyWJc5Y435D17XQ9QU5A= @@ -2302,8 +2304,6 @@ 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.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= @@ -2496,6 +2496,7 @@ github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -2822,6 +2823,7 @@ github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwS github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= @@ -2890,8 +2892,6 @@ github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2 h1 github.com/luanshaotong/nerdctl/mod/tigron v0.0.0-20250717075412-9690955bbfc2/go.mod h1:gmUZh2wUVxr/msGogKUi6v9eJbP5ASO4fVYEPzHH4iI= github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2 h1:Xd/Ucu301cld+j+E/+nf0aeQ5HOwmZfJ+WH2dCjCy3Y= github.com/luanshaotong/nerdctl/v2 v2.0.0-20250717075412-9690955bbfc2/go.mod h1:un/Eqg9DrVwazXHukMsxq+PEBixrN7YC3NRXdx5J3tY= -github.com/luanshaotong/nerdctl/v2 v2.0.0-20250820041017-57f3084bf6c6 h1:+21lTTtGxNsTEDgUyo5jhKVNbz9kgq+2GH8dWjByU2E= -github.com/luanshaotong/nerdctl/v2 v2.0.0-20250820041017-57f3084bf6c6/go.mod h1:G559iiQz5CBnDv0dBHJUHyUOCGYwhpjxhkeAZPPXOSQ= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= @@ -2996,6 +2996,7 @@ github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YO github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= @@ -3043,6 +3044,7 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olareg/olareg v0.1.2/go.mod h1:TWs+N6pO1S4bdB6eerzUm/ITRQ6kw91mVf9ZYeGtw+Y= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -3259,6 +3261,7 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg= github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= @@ -3418,8 +3421,6 @@ github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83 h1:9AUN7+NK4IV+A11igqjQM github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= -github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= @@ -3428,6 +3429,7 @@ github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0= +github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -3485,6 +3487,7 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973 h1:iCnkJ/qjKZGdZnlcj1N55AxPDan814kpc3s1cDpQKd8= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 h1:p7OofyZ509h8DmPLh8Hn+EIIZm/xYhdZHJ9GnXHdr6U= github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= @@ -3729,6 +3732,7 @@ go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= From 819ae02e8bd6044c3fbc4ef1e3b4088f3f8ff706 Mon Sep 17 00:00:00 2001 From: yy Date: Wed, 27 Aug 2025 14:52:44 +0800 Subject: [PATCH 31/46] fix target state change --- .../devbox/internal/controller/state_change_handler.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 9a0622f49bdd..1896d846c47d 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -71,7 +71,7 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er h.Logger.Info("commit already in progress, skipping duplicate request", "devbox", devbox.Name, "contentID", devbox.Status.ContentID) return nil } - if err := h.commitDevbox(ctx, devbox); err != nil { + if err := h.commitDevbox(ctx, devbox, targetState); err != nil { h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) return err } @@ -87,7 +87,7 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er return nil } -func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha2.Devbox) error { +func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha2.Devbox, targetState devboxv1alpha2.DevboxState) error { // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id // step 0: set commit status to committing to prevent duplicate requests devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha2.CommitStatusCommitting @@ -139,7 +139,7 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha2.CommitStatusSuccess devbox.Status.CommitRecords[devbox.Status.ContentID].CommitTime = metav1.Now() // step 3: update devbox status state to shutdown - devbox.Status.State = devboxv1alpha2.DevboxStateShutdown + devbox.Status.State = targetState // step 4: add a new commit record for the new content id // make sure that always have a new commit record for shutdown state devbox.Status.ContentID = uuid.New().String() From 7ae4c04cf192df0b4ba826458483f552a943c8f7 Mon Sep 17 00:00:00 2001 From: yy Date: Fri, 29 Aug 2025 14:37:53 +0800 Subject: [PATCH 32/46] add print and fix logic --- .../devbox/api/v1alpha2/devbox_types.go | 3 + .../crd/bases/devbox.sealos.io_devboxes.yaml | 5 + .../devbox.sealos.io_devboxreleases.yaml | 7 +- .../devbox_v1alpha2_devboxrelease.yaml | 4 +- .../internal/controller/devbox_controller.go | 125 +++++++++++++++--- .../controller/state_change_handler.go | 9 +- 6 files changed, 131 insertions(+), 22 deletions(-) diff --git a/controllers/devbox/api/v1alpha2/devbox_types.go b/controllers/devbox/api/v1alpha2/devbox_types.go index 2d4573806147..6e61fa8c38a2 100644 --- a/controllers/devbox/api/v1alpha2/devbox_types.go +++ b/controllers/devbox/api/v1alpha2/devbox_types.go @@ -253,6 +253,8 @@ type DevboxStatus struct { // +kubebuilder:validation:Optional ContentID string `json:"contentID"` // +kubebuilder:validation:Optional + Node string `json:"node"` + // +kubebuilder:validation:Optional // +kubebuilder:default=Running State DevboxState `json:"state"` // CommitRecords is the records of the devbox commits @@ -269,6 +271,7 @@ type DevboxStatus struct { // +kubebuilder:printcolumn:name="NetworkType",type="string",JSONPath=".status.network.type" // +kubebuilder:printcolumn:name="NodePort",type="integer",JSONPath=".status.network.nodePort" // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" +// +kubebuilder:printcolumn:name="Node",type="string",JSONPath=".status.node" // Devbox is the Schema for the devboxes API type Devbox struct { diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index 811c8532a06a..e23e634bbfde 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -27,6 +27,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .status.node + name: Node + type: string name: v1alpha2 schema: openAPIV3Schema: @@ -3293,6 +3296,8 @@ spec: required: - type type: object + node: + type: string phase: type: string state: diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml index 73b37bfe9650..ef6ec4bc089b 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml @@ -55,9 +55,10 @@ spec: type: string phase: type: string - required: - - originalDevboxState - - phase + sourceImage: + type: string + targetImage: + type: string type: object type: object served: true diff --git a/controllers/devbox/config/samples/devbox_v1alpha2_devboxrelease.yaml b/controllers/devbox/config/samples/devbox_v1alpha2_devboxrelease.yaml index 39de824d5090..c096e47a0702 100644 --- a/controllers/devbox/config/samples/devbox_v1alpha2_devboxrelease.yaml +++ b/controllers/devbox/config/samples/devbox_v1alpha2_devboxrelease.yaml @@ -6,4 +6,6 @@ metadata: app.kubernetes.io/managed-by: kustomize name: devboxrelease-sample spec: - # TODO(user): Add fields here + devboxName: devbox-sample-1 + version: 1.0.0 + notes: "This is a sample devbox release" diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index a756a11e2bef..5e298f423f15 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -164,6 +164,7 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr "acceptanceThreshold", r.AcceptanceThreshold) // set up devbox node and content id, new a record for the devbox devbox.Status.CommitRecords[devbox.Status.ContentID].Node = r.NodeName + devbox.Status.Node = r.NodeName if err := r.Status().Update(ctx, devbox); err != nil { logger.Info("try to schedule devbox to node failed. This devbox may have already been scheduled to another node", "error", err) return ctrl.Result{}, err @@ -205,11 +206,10 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Sync service success", "Sync service success") } - // sync devbox state - if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == r.NodeName && r.syncDevboxState(ctx, devbox) { - logger.Info("devbox state changed, wait for state change handler to handle the event, requeue after 5 seconds", "from", devbox.Status.State, "to", devbox.Spec.State) - r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) - return ctrl.Result{RequeueAfter: 5 * time.Second}, nil + // sync devbox phase based on desired state and current pod status + if err := r.syncDevboxPhase(ctx, devbox, recLabels); err != nil { + logger.Error(err, "sync devbox phase failed") + return ctrl.Result{}, err } // create or update pod @@ -222,6 +222,15 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr logger.Info("sync pod success") r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Sync pod success", "Sync pod success") + // sync devbox state + if r.syncDevboxState(ctx, devbox) { + logger.Info("devbox state changed, wait for state change handler to handle the event, requeue after 5 seconds", "from", devbox.Status.State, "to", devbox.Spec.State) + logger.Info("recording state change event", "devbox", devbox.Name, "nodeName", r.NodeName) + r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) + r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) + return ctrl.Result{RequeueAfter: 5 * time.Second}, nil + } + logger.Info("devbox reconcile success") return ctrl.Result{}, nil } @@ -345,14 +354,14 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha2.D func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alpha2.Devbox, recLabels map[string]string) error { var servicePorts []corev1.ServicePort - // for _, port := range devbox.Spec.Config.Ports { - // servicePorts = append(servicePorts, corev1.ServicePort{ - // Name: port.Name, - // Port: port.ContainerPort, - // TargetPort: intstr.FromInt32(port.ContainerPort), - // Protocol: port.Protocol, - // }) - // } + for _, port := range devbox.Spec.Config.Ports { + servicePorts = append(servicePorts, corev1.ServicePort{ + Name: port.Name, + Port: port.ContainerPort, + TargetPort: intstr.FromInt32(port.ContainerPort), + Protocol: port.Protocol, + }) + } if len(servicePorts) == 0 { //use the default value servicePorts = []corev1.ServicePort{ @@ -434,6 +443,92 @@ func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alph return nil } +// syncDevboxPhase updates devbox.Status.Phase derived from desired state and current pod status +func (r *DevboxReconciler) syncDevboxPhase(ctx context.Context, devbox *devboxv1alpha2.Devbox, recLabels map[string]string) error { + logger := log.FromContext(ctx) + podList := &corev1.PodList{} + if err := r.List(ctx, podList, client.InNamespace(devbox.Namespace), client.MatchingLabels(recLabels)); err != nil { + return err + } + err := r.Get(ctx, client.ObjectKey{Namespace: devbox.Namespace, Name: devbox.Name}, devbox) + if err != nil { + return fmt.Errorf("failed to get devbox: %w", err) + } + + var ( + hasRunning bool + hasPending bool + hasFailed bool + hasTerminating bool + ) + for i := range podList.Items { + p := &podList.Items[i] + if !p.DeletionTimestamp.IsZero() { + hasTerminating = true + } + switch p.Status.Phase { + case corev1.PodRunning: + hasRunning = true + case corev1.PodPending: + hasPending = true + case corev1.PodFailed: + hasFailed = true + } + } + + derivePhase := func() devboxv1alpha2.DevboxPhase { + // Any explicit failure maps to Error + if hasFailed { + return devboxv1alpha2.DevboxPhaseError + } + switch devbox.Spec.State { + case devboxv1alpha2.DevboxStateRunning: + if hasRunning { + return devboxv1alpha2.DevboxPhaseRunning + } + if hasPending || len(podList.Items) > 0 { + return devboxv1alpha2.DevboxPhasePending + } + // no pod yet, but desired Running -> Pending + return devboxv1alpha2.DevboxPhasePending + case devboxv1alpha2.DevboxStatePaused: + if len(podList.Items) > 0 { + if hasTerminating { + return devboxv1alpha2.DevboxPhasePausing + } + return devboxv1alpha2.DevboxPhasePausing + } + return devboxv1alpha2.DevboxPhasePaused + case devboxv1alpha2.DevboxStateStopped: + if len(podList.Items) > 0 { + if hasTerminating { + return devboxv1alpha2.DevboxPhaseStopping + } + return devboxv1alpha2.DevboxPhaseStopping + } + return devboxv1alpha2.DevboxPhaseStopped + case devboxv1alpha2.DevboxStateShutdown: + if len(podList.Items) > 0 { + if hasTerminating { + return devboxv1alpha2.DevboxPhaseShutting + } + return devboxv1alpha2.DevboxPhaseShutting + } + return devboxv1alpha2.DevboxPhaseShutdown + default: + return devboxv1alpha2.DevboxPhaseUnknown + } + } + + newPhase := derivePhase() + if devbox.Status.Phase == newPhase { + return nil + } + logger.Info("updating devbox phase", "from", devbox.Status.Phase, "to", newPhase) + devbox.Status.Phase = newPhase + return r.Status().Update(ctx, devbox) +} + // sync devbox state, and record the state change event to state change recorder, state change handler will handle the event func (r *DevboxReconciler) syncDevboxState(ctx context.Context, devbox *devboxv1alpha2.Devbox) bool { logger := log.FromContext(ctx) @@ -448,10 +543,6 @@ func (r *DevboxReconciler) syncDevboxState(ctx context.Context, devbox *devboxv1 "from", devbox.Status.State, "to", devbox.Spec.State, "devbox", devbox.Name) - logger.Info("recording state change event", - "devbox", devbox.Name, - "nodeName", r.NodeName) - r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) return true } logger.Info("devbox state unchanged", diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 1896d846c47d..363b3baf4a88 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -3,6 +3,7 @@ package controller import ( "context" "fmt" + "sync" "time" "github.com/go-logr/logr" @@ -20,6 +21,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) +var commitMap = sync.Map{} + type StateChangeHandler struct { Committer commit.Committer CommitImageRegistry string @@ -67,11 +70,13 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er if needsCommit { // Check if commit is already in progress to prevent duplicate requests - if devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus == devboxv1alpha2.CommitStatusCommitting { + if _, ok := commitMap.Load(devbox.Status.ContentID); ok { h.Logger.Info("commit already in progress, skipping duplicate request", "devbox", devbox.Name, "contentID", devbox.Status.ContentID) return nil } + commitMap.Store(devbox.Status.ContentID, true) if err := h.commitDevbox(ctx, devbox, targetState); err != nil { + commitMap.Delete(devbox.Status.ContentID) h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) return err } @@ -88,6 +93,7 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er } func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha2.Devbox, targetState devboxv1alpha2.DevboxState) error { + defer commitMap.Delete(devbox.Status.ContentID) // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id // step 0: set commit status to committing to prevent duplicate requests devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha2.CommitStatusCommitting @@ -150,6 +156,7 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a CommitImage: h.generateImageName(devbox), GenerateTime: metav1.Now(), } + devbox.Status.Node = "" h.Logger.Info("update devbox status to shutdown", "devbox", devbox.Name) if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { return h.Client.Status().Update(ctx, devbox) From 7e5bb588725f1ecaeb54ea17113bb469b4297409 Mon Sep 17 00:00:00 2001 From: yy Date: Fri, 29 Aug 2025 17:06:10 +0800 Subject: [PATCH 33/46] fix release controller --- .../api/v1alpha2/devboxrelease_types.go | 6 +++++ .../devbox.sealos.io_devboxreleases.yaml | 17 +++++++++++- .../controller/devboxrelease_controller.go | 27 ++++++++++++------- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/controllers/devbox/api/v1alpha2/devboxrelease_types.go b/controllers/devbox/api/v1alpha2/devboxrelease_types.go index caef50e5338c..9a985acf6d1f 100644 --- a/controllers/devbox/api/v1alpha2/devboxrelease_types.go +++ b/controllers/devbox/api/v1alpha2/devboxrelease_types.go @@ -42,6 +42,9 @@ const ( ) type DevboxReleaseStatus struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Success;Pending;Failed Phase DevboxReleasePhase `json:"phase,omitempty"` OriginalDevboxState DevboxState `json:"originalDevboxState,omitempty"` SourceImage string `json:"sourceImage,omitempty"` @@ -50,6 +53,9 @@ type DevboxReleaseStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" +// +kubebuilder:printcolumn:name="SourceImage",type="string",JSONPath=".status.sourceImage" +// +kubebuilder:printcolumn:name="TargetImage",type="string",JSONPath=".status.targetImage" // DevboxRelease is the Schema for the devboxreleases API. type DevboxRelease struct { diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml index ef6ec4bc089b..f211be9112d1 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml @@ -14,7 +14,17 @@ spec: singular: devboxrelease scope: Namespaced versions: - - name: v1alpha2 + - additionalPrinterColumns: + - jsonPath: .status.phase + name: Phase + type: string + - jsonPath: .status.sourceImage + name: SourceImage + type: string + - jsonPath: .status.targetImage + name: TargetImage + type: string + name: v1alpha2 schema: openAPIV3Schema: description: DevboxRelease is the Schema for the devboxreleases API. @@ -54,6 +64,11 @@ spec: originalDevboxState: type: string phase: + default: Pending + enum: + - Success + - Pending + - Failed type: string sourceImage: type: string diff --git a/controllers/devbox/internal/controller/devboxrelease_controller.go b/controllers/devbox/internal/controller/devboxrelease_controller.go index e39cf571a8f1..c127ddde6e7f 100644 --- a/controllers/devbox/internal/controller/devboxrelease_controller.go +++ b/controllers/devbox/internal/controller/devboxrelease_controller.go @@ -67,15 +67,21 @@ func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reques logger.Info("Reconciling DevBoxRelease", "devbox", devboxRelease.Spec.DevboxName, "version", devboxRelease.Spec.Version, "phase", devboxRelease.Status.Phase) + devbox := &devboxv1alpha2.Devbox{} + if err := r.Get(ctx, client.ObjectKey{Namespace: devboxRelease.Namespace, Name: devboxRelease.Spec.DevboxName}, devbox); err != nil { + logger.Error(err, "Failed to get devbox", "devbox", devboxRelease.Spec.DevboxName) + return ctrl.Result{}, err + } + + // if devbox is running, skip release + if devbox.Status.State == devboxv1alpha2.DevboxStateRunning || devbox.Status.State == devboxv1alpha2.DevboxStatePaused { + logger.Info("Devbox is running or paused, skipping release", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + return ctrl.Result{RequeueAfter: time.Second * 10}, nil + } if devboxRelease.Status.Phase == "" { devboxRelease.Status.Phase = devboxv1alpha2.DevboxReleasePhasePending - devbox := &devboxv1alpha2.Devbox{} - if err := r.Get(ctx, req.NamespacedName, devbox); err != nil { - logger.Error(err, "Failed to get devbox", "devbox", devboxRelease.Spec.DevboxName) - return ctrl.Result{}, err - } devboxRelease.Status.OriginalDevboxState = devbox.Spec.State - devboxRelease.Status.SourceImage = devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage + devboxRelease.Status.SourceImage = devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage devboxRelease.Status.TargetImage = fmt.Sprintf("%s/%s/%s:%s", r.Registry.Host, devboxRelease.Namespace, devboxRelease.Spec.DevboxName, devboxRelease.Spec.Version) if err := r.Status().Update(ctx, devboxRelease); err != nil { logger.Error(err, "Failed to update status", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) @@ -83,7 +89,6 @@ func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reques } return ctrl.Result{Requeue: true}, nil } - if devboxRelease.Status.Phase == devboxv1alpha2.DevboxReleasePhasePending { logger.Info("Creating release tag", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) err := r.Release(ctx, devboxRelease) @@ -107,9 +112,13 @@ func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reques return ctrl.Result{}, nil } -// todo: implement this function func (r *DevboxreleaseReconciler) Release(ctx context.Context, devboxRelease *devboxv1alpha2.DevboxRelease) error { - r.Registry.ReTag(devboxRelease.Status.SourceImage, devboxRelease.Status.TargetImage) + logger := log.FromContext(ctx) + if err := r.Registry.ReTag(devboxRelease.Status.SourceImage, devboxRelease.Status.TargetImage); err != nil { + logger.Error(err, "Failed to re-tag image", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + return err + } + logger.Info("Image re-tagged", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) return nil } From 448c5b00d34d7e53616dfd92bea1bd8dc394a150 Mon Sep 17 00:00:00 2001 From: yy Date: Tue, 2 Sep 2025 11:14:44 +0800 Subject: [PATCH 34/46] fix status update when commit --- controllers/devbox/api/v1alpha2/devbox_types.go | 4 ---- .../internal/controller/state_change_handler.go | 14 +++++++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/controllers/devbox/api/v1alpha2/devbox_types.go b/controllers/devbox/api/v1alpha2/devbox_types.go index 6e61fa8c38a2..6558e74153ef 100644 --- a/controllers/devbox/api/v1alpha2/devbox_types.go +++ b/controllers/devbox/api/v1alpha2/devbox_types.go @@ -133,10 +133,6 @@ type DevboxSpec struct { // +kubebuilder:validation:Required Resource corev1.ResourceList `json:"resource"` - // +kubebuilder:validation:Optional - // +kubebuilder:default=false - Squash bool `json:"squash"` - // +kubebuilder:validation:Required Image string `json:"image"` diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 363b3baf4a88..907c188f5268 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -94,6 +94,10 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha2.Devbox, targetState devboxv1alpha2.DevboxState) error { defer commitMap.Delete(devbox.Status.ContentID) + if err := h.Client.Get(ctx, types.NamespacedName{Namespace: devbox.Namespace, Name: devbox.Name}, devbox); err != nil { + h.Logger.Error(err, "failed to get devbox", "devbox", devbox.Name) + return err + } // do commit, update devbox commit record, update devbox status state to shutdown, add a new commit record for the new content id // step 0: set commit status to committing to prevent duplicate requests devbox.Status.CommitRecords[devbox.Status.ContentID].CommitStatus = devboxv1alpha2.CommitStatusCommitting @@ -104,6 +108,10 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a } h.Logger.Info("set commit status to committing", "devbox", devbox.Name, "contentID", devbox.Status.ContentID) + if err := h.Client.Get(ctx, types.NamespacedName{Namespace: devbox.Namespace, Name: devbox.Name}, devbox); err != nil { + h.Logger.Error(err, "failed to get devbox", "devbox", devbox.Name) + return err + } // step 1: do commit, push image, remove container whether commit success or not baseImage := devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage commitImage := devbox.Status.CommitRecords[devbox.Status.ContentID].CommitImage @@ -127,6 +135,10 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a } return err } + if err := h.Client.Get(ctx, types.NamespacedName{Namespace: devbox.Namespace, Name: devbox.Name}, devbox); err != nil { + h.Logger.Error(err, "failed to get devbox", "devbox", devbox.Name) + return err + } if err := h.Committer.Push(ctx, commitImage); err != nil { h.Logger.Error(err, "failed to push commit image", "commitImage", commitImage) // Update commit status to failed on push error @@ -158,7 +170,7 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a } devbox.Status.Node = "" h.Logger.Info("update devbox status to shutdown", "devbox", devbox.Name) - if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { + if err := retry.RetryOnConflict(retry.DefaultRetry, func() error { return h.Client.Status().Update(ctx, devbox) }); err != nil { h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) From be6c2a1e535e8c95ea9d9a76370c9132e1da81d1 Mon Sep 17 00:00:00 2001 From: yy Date: Tue, 2 Sep 2025 14:52:37 +0800 Subject: [PATCH 35/46] add last container status --- .../devbox/api/v1alpha2/devbox_types.go | 3 + .../crd/bases/devbox.sealos.io_devboxes.yaml | 378 +++++++++++++++++- .../internal/controller/devbox_controller.go | 36 +- 3 files changed, 408 insertions(+), 9 deletions(-) diff --git a/controllers/devbox/api/v1alpha2/devbox_types.go b/controllers/devbox/api/v1alpha2/devbox_types.go index 6558e74153ef..61fe9ca57a2b 100644 --- a/controllers/devbox/api/v1alpha2/devbox_types.go +++ b/controllers/devbox/api/v1alpha2/devbox_types.go @@ -259,6 +259,9 @@ type DevboxStatus struct { Phase DevboxPhase `json:"phase"` // +kubebuilder:validation:Optional Network NetworkStatus `json:"network"` + + // +kubebuilder:validation:Optional + LastContainerStatus corev1.ContainerStatus `json:"lastContainerStatus"` } // +kubebuilder:object:root=true diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index e23e634bbfde..d8c8cf44f281 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -3168,9 +3168,6 @@ spec: type: object runtimeClassName: type: string - squash: - default: false - type: boolean state: default: Running enum: @@ -3279,6 +3276,381 @@ spec: type: object contentID: type: string + lastContainerStatus: + description: ContainerStatus contains details for the current status + of this container. + properties: + allocatedResources: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + AllocatedResources represents the compute resources allocated for this container by the + node. Kubelet sets this value to Container.Resources.Requests upon successful pod admission + and after successfully admitting desired pod resize. + type: object + allocatedResourcesStatus: + description: |- + AllocatedResourcesStatus represents the status of various resources + allocated for this Pod. + items: + description: ResourceStatus represents the status of a single + resource allocated to a Pod. + properties: + name: + description: |- + Name of the resource. Must be unique within the pod and in case of non-DRA resource, match one of the resources from the pod spec. + For DRA resources, the value must be "claim:/". + When this status is reported about a container, the "claim_name" and "request" must match one of the claims of this container. + type: string + resources: + description: |- + List of unique resources health. Each element in the list contains an unique resource ID and its health. + At a minimum, for the lifetime of a Pod, resource ID must uniquely identify the resource allocated to the Pod on the Node. + If other Pod on the same Node reports the status with the same resource ID, it must be the same resource they share. + See ResourceID type definition for a specific format it has in various use cases. + items: + description: |- + ResourceHealth represents the health of a resource. It has the latest device health information. + This is a part of KEP https://kep.k8s.io/4680. + properties: + health: + description: |- + Health of the resource. + can be one of: + - Healthy: operates as normal + - Unhealthy: reported unhealthy. We consider this a temporary health issue + since we do not have a mechanism today to distinguish + temporary and permanent issues. + - Unknown: The status cannot be determined. + For example, Device Plugin got unregistered and hasn't been re-registered since. + + In future we may want to introduce the PermanentlyUnhealthy Status. + type: string + resourceID: + description: ResourceID is the unique identifier of + the resource. See the ResourceID type for more information. + type: string + required: + - resourceID + type: object + type: array + x-kubernetes-list-map-keys: + - resourceID + x-kubernetes-list-type: map + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + containerID: + description: |- + ContainerID is the ID of the container in the format '://'. + Where type is a container runtime identifier, returned from Version call of CRI API + (for example "containerd"). + type: string + image: + description: |- + Image is the name of container image that the container is running. + The container image may not match the image used in the PodSpec, + as it may have been resolved by the runtime. + More info: https://kubernetes.io/docs/concepts/containers/images. + type: string + imageID: + description: |- + ImageID is the image ID of the container's image. The image ID may not + match the image ID of the image used in the PodSpec, as it may have been + resolved by the runtime. + type: string + lastState: + description: |- + LastTerminationState holds the last termination state of the container to + help debug container crashes and restarts. This field is not + populated if the container is still running and RestartCount is 0. + properties: + running: + description: Details about a running container + properties: + startedAt: + description: Time at which the container was last (re-)started + format: date-time + type: string + type: object + terminated: + description: Details about a terminated container + properties: + containerID: + description: Container's ID in the format '://' + type: string + exitCode: + description: Exit status from the last termination of + the container + format: int32 + type: integer + finishedAt: + description: Time at which the container last terminated + format: date-time + type: string + message: + description: Message regarding the last termination of + the container + type: string + reason: + description: (brief) reason from the last termination + of the container + type: string + signal: + description: Signal from the last termination of the container + format: int32 + type: integer + startedAt: + description: Time at which previous execution of the container + started + format: date-time + type: string + required: + - exitCode + type: object + waiting: + description: Details about a waiting container + properties: + message: + description: Message regarding why the container is not + yet running. + type: string + reason: + description: (brief) reason the container is not yet running. + type: string + type: object + type: object + name: + description: |- + Name is a DNS_LABEL representing the unique name of the container. + Each container in a pod must have a unique name across all container types. + Cannot be updated. + type: string + ready: + description: |- + Ready specifies whether the container is currently passing its readiness check. + The value will change as readiness probes keep executing. If no readiness + probes are specified, this field defaults to true once the container is + fully started (see Started field). + + The value is typically used to determine whether a container is ready to + accept traffic. + type: boolean + resources: + description: |- + Resources represents the compute resource requests and limits that have been successfully + enacted on the running container after it has been started or has been successfully resized. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartCount: + description: |- + RestartCount holds the number of times the container has been restarted. + Kubelet makes an effort to always increment the value, but there + are cases when the state may be lost due to node restarts and then the value + may be reset to 0. The value is never negative. + format: int32 + type: integer + started: + description: |- + Started indicates whether the container has finished its postStart lifecycle hook + and passed its startup probe. + Initialized as false, becomes true after startupProbe is considered + successful. Resets to false when the container is restarted, or if kubelet + loses state temporarily. In both cases, startup probes will run again. + Is always true when no startupProbe is defined and container is running and + has passed the postStart lifecycle hook. The null value must be treated the + same as false. + type: boolean + state: + description: State holds details about the container's current + condition. + properties: + running: + description: Details about a running container + properties: + startedAt: + description: Time at which the container was last (re-)started + format: date-time + type: string + type: object + terminated: + description: Details about a terminated container + properties: + containerID: + description: Container's ID in the format '://' + type: string + exitCode: + description: Exit status from the last termination of + the container + format: int32 + type: integer + finishedAt: + description: Time at which the container last terminated + format: date-time + type: string + message: + description: Message regarding the last termination of + the container + type: string + reason: + description: (brief) reason from the last termination + of the container + type: string + signal: + description: Signal from the last termination of the container + format: int32 + type: integer + startedAt: + description: Time at which previous execution of the container + started + format: date-time + type: string + required: + - exitCode + type: object + waiting: + description: Details about a waiting container + properties: + message: + description: Message regarding why the container is not + yet running. + type: string + reason: + description: (brief) reason the container is not yet running. + type: string + type: object + type: object + user: + description: User represents user identity information initially + attached to the first process of the container + properties: + linux: + description: |- + Linux holds user identity information initially attached to the first process of the containers in Linux. + Note that the actual running identity can be changed if the process has enough privilege to do so. + properties: + gid: + description: GID is the primary gid initially attached + to the first process in the container + format: int64 + type: integer + supplementalGroups: + description: SupplementalGroups are the supplemental groups + initially attached to the first process in the container + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + uid: + description: UID is the primary uid initially attached + to the first process in the container + format: int64 + type: integer + required: + - gid + - uid + type: object + type: object + volumeMounts: + description: Status of volume mounts. + items: + description: VolumeMountStatus shows status of volume mounts. + properties: + mountPath: + description: MountPath corresponds to the original VolumeMount. + type: string + name: + description: Name corresponds to the name of the original + VolumeMount. + type: string + readOnly: + description: ReadOnly corresponds to the original VolumeMount. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly must be set to Disabled, Enabled, or unspecified (for non-readonly mounts). + An IfPossible value in the original VolumeMount must be translated to Disabled or Enabled, + depending on the mount result. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + required: + - image + - imageID + - name + - ready + - restartCount + type: object network: properties: nodePort: diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 5e298f423f15..24dfdf0d972c 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -326,16 +326,16 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha2.D case 1: // skip if pod is already created if !podList.Items[0].DeletionTimestamp.IsZero() { - return r.handlePodDeleted(ctx, &podList.Items[0]) + return r.handlePodDeleted(ctx, devbox, &podList.Items[0]) } if podList.Items[0].Status.Phase != corev1.PodRunning && podList.Items[0].Status.Phase != corev1.PodPending { - return r.deletePod(ctx, &podList.Items[0]) + return r.deletePod(ctx, devbox, &podList.Items[0]) } return nil default: // more than one pod found, remove finalizer and delete them for _, pod := range podList.Items { - r.deletePod(ctx, &pod) + r.deletePod(ctx, devbox, &pod) } logger.Error(fmt.Errorf("more than one pod found"), "more than one pod found") r.Recorder.Eventf(devbox, corev1.EventTypeWarning, "More than one pod found", "More than one pod found") @@ -344,7 +344,7 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha2.D case devboxv1alpha2.DevboxStatePaused, devboxv1alpha2.DevboxStateStopped, devboxv1alpha2.DevboxStateShutdown: if len(podList.Items) > 0 { for _, pod := range podList.Items { - r.deletePod(ctx, &pod) + r.deletePod(ctx, devbox, &pod) } } return nil @@ -551,8 +551,20 @@ func (r *DevboxReconciler) syncDevboxState(ctx context.Context, devbox *devboxv1 return false } -func (r *DevboxReconciler) deletePod(ctx context.Context, pod *corev1.Pod) error { +func (r *DevboxReconciler) deletePod(ctx context.Context, devbox *devboxv1alpha2.Devbox, pod *corev1.Pod) error { logger := log.FromContext(ctx) + err := r.Client.Get(ctx, client.ObjectKey{Namespace: pod.Namespace, Name: pod.Name}, devbox) + if err != nil { + logger.Error(err, "failed to get devbox") + return err + } + if len(pod.Status.ContainerStatuses) > 0 { + devbox.Status.LastContainerStatus = pod.Status.ContainerStatuses[0] + if err := r.Status().Update(ctx, devbox); err != nil { + logger.Error(err, "failed to update devbox status") + return err + } + } // remove finalizer and delete pod controllerutil.RemoveFinalizer(pod, devboxv1alpha2.FinalizerName) if err := r.Update(ctx, pod); err != nil { @@ -566,13 +578,25 @@ func (r *DevboxReconciler) deletePod(ctx context.Context, pod *corev1.Pod) error return nil } -func (r *DevboxReconciler) handlePodDeleted(ctx context.Context, pod *corev1.Pod) error { +func (r *DevboxReconciler) handlePodDeleted(ctx context.Context, devbox *devboxv1alpha2.Devbox, pod *corev1.Pod) error { logger := log.FromContext(ctx) controllerutil.RemoveFinalizer(pod, devboxv1alpha2.FinalizerName) if err := r.Update(ctx, pod); err != nil { logger.Error(err, "remove finalizer failed") return err } + err := r.Client.Get(ctx, client.ObjectKey{Namespace: pod.Namespace, Name: pod.Name}, devbox) + if err != nil { + logger.Error(err, "failed to get devbox") + return err + } + if len(pod.Status.ContainerStatuses) > 0 { + devbox.Status.LastContainerStatus = pod.Status.ContainerStatuses[0] + if err := r.Status().Update(ctx, devbox); err != nil { + logger.Error(err, "failed to update devbox status") + return err + } + } return nil } From 87b1c276341a41e3675b97444e3242e2e80672e0 Mon Sep 17 00:00:00 2001 From: Yun Pan Date: Fri, 5 Sep 2025 16:34:04 +0800 Subject: [PATCH 36/46] add error handling for event processing and pod deletion Signed-off-by: Yun Pan --- controllers/devbox/cmd/main.go | 4 +++- .../devbox/internal/controller/devbox_controller.go | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index ed28e095db25..4d3d9dea4582 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -307,7 +307,9 @@ func main() { "eventSourceHost", event.Source.Host, "eventType", event.Type, "eventReason", event.Reason) - stateChangeHandler.Handle(context.Background(), event) + if err := stateChangeHandler.Handle(context.TODO(), event); err != nil { + setupLog.Error(err, "failed to handle event", "event", event.Name) + } }) defer watcher.Stop() diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 24dfdf0d972c..a9cf394527a5 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -335,7 +335,10 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha2.D default: // more than one pod found, remove finalizer and delete them for _, pod := range podList.Items { - r.deletePod(ctx, devbox, &pod) + if err := r.deletePod(ctx, devbox, &pod); err != nil { + logger.Error(err, "failed to delete pod", "pod", pod.Name) + return err + } } logger.Error(fmt.Errorf("more than one pod found"), "more than one pod found") r.Recorder.Eventf(devbox, corev1.EventTypeWarning, "More than one pod found", "More than one pod found") @@ -344,7 +347,10 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha2.D case devboxv1alpha2.DevboxStatePaused, devboxv1alpha2.DevboxStateStopped, devboxv1alpha2.DevboxStateShutdown: if len(podList.Items) > 0 { for _, pod := range podList.Items { - r.deletePod(ctx, devbox, &pod) + if err := r.deletePod(ctx, devbox, &pod); err != nil { + logger.Error(err, "failed to delete pod", "pod", pod.Name) + return err + } } } return nil From 91388d747216a92703f76bfe919b493ade45eae7 Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Fri, 5 Sep 2025 16:53:40 +0800 Subject: [PATCH 37/46] handler fix (#60) Co-authored-by: Cunzili --- controllers/devbox/internal/controller/devbox_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index a9cf394527a5..d7a5d16ff512 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -223,7 +223,7 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Sync pod success", "Sync pod success") // sync devbox state - if r.syncDevboxState(ctx, devbox) { + if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == r.NodeName && r.syncDevboxState(ctx, devbox) { logger.Info("devbox state changed, wait for state change handler to handle the event, requeue after 5 seconds", "from", devbox.Status.State, "to", devbox.Spec.State) logger.Info("recording state change event", "devbox", devbox.Name, "nodeName", r.NodeName) r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) From 5876514d385873b0a22a9da6990de18d090ce709 Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:55:47 +0800 Subject: [PATCH 38/46] fix(commit): remove lv during delete devbox (#63) * remove lv after delete devbox remove lv during delete devbox remove lv during delete devbox * extend event handler --------- Co-authored-by: Cunzili --- controllers/devbox/internal/commit/commit.go | 1 + .../internal/controller/devbox_controller.go | 53 ++++++- .../controller/state_change_handler.go | 136 +++++++++++++++++- .../internal/controller/utils/events/const.go | 8 ++ 4 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 controllers/devbox/internal/controller/utils/events/const.go diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 3b762d947200..2ce55f2d6ed5 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -28,6 +28,7 @@ import ( ) type Committer interface { + CreateContainer(ctx context.Context, devboxName string, contentID string, baseImage string) (string, error) Commit(ctx context.Context, devboxName string, contentID string, baseImage string, commitImage string) (string, error) Push(ctx context.Context, imageName string) error RemoveImage(ctx context.Context, imageName string, force bool, async bool) error diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index d7a5d16ff512..3a30d4199ccf 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -26,6 +26,7 @@ import ( devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" "github.com/labring/sealos/controllers/devbox/internal/commit" "github.com/labring/sealos/controllers/devbox/internal/controller/helper" + "github.com/labring/sealos/controllers/devbox/internal/controller/utils/events" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" "github.com/labring/sealos/controllers/devbox/internal/stat" @@ -123,6 +124,11 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, err } + // delete storage: + if err := r.handleStorageDeleted(ctx, devbox); err != nil { + return ctrl.Result{}, err + } + logger.Info("devbox deleted, remove finalizer") if controllerutil.RemoveFinalizer(devbox, devboxv1alpha2.FinalizerName) { if err := r.Update(ctx, devbox); err != nil { @@ -226,8 +232,10 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if devbox.Status.CommitRecords[devbox.Status.ContentID].Node == r.NodeName && r.syncDevboxState(ctx, devbox) { logger.Info("devbox state changed, wait for state change handler to handle the event, requeue after 5 seconds", "from", devbox.Status.State, "to", devbox.Spec.State) logger.Info("recording state change event", "devbox", devbox.Name, "nodeName", r.NodeName) - r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) - r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Devbox state changed", "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) + + r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, events.EventReasonDevboxStateChanged, "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) + r.Recorder.Eventf(devbox, corev1.EventTypeNormal, events.EventReasonDevboxStateChanged, "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) + return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } @@ -606,6 +614,47 @@ func (r *DevboxReconciler) handlePodDeleted(ctx context.Context, devbox *devboxv return nil } +func (r *DevboxReconciler) handleStorageDeleted(ctx context.Context, devbox *devboxv1alpha2.Devbox) error { + logger := log.FromContext(ctx) + + // check status + if devbox.Spec.State == devboxv1alpha2.DevboxStateShutdown || devbox.Spec.State == devboxv1alpha2.DevboxStateStopped { + logger.Info("devbox's spec state is stopped or shutdown, storage already cleaned up", "devbox", devbox.Name, "devboxSpecState", devbox.Spec.State) + return nil + } + + if devbox.Status.CommitRecords[devbox.Status.ContentID].Node != r.NodeName { + logger.Info("devbox's commit record node is not the same as the node name, skipping storage cleanup", "devbox", devbox.Name, "commitRecordNode", devbox.Status.CommitRecords[devbox.Status.ContentID].Node, "nodeName", r.NodeName) + return nil + } + + contentID := devbox.Status.ContentID + if contentID == "" { + logger.Error(fmt.Errorf("contentID is empty"), "devbox", devbox.Name, "devboxSpecState", devbox.Spec.State) + return fmt.Errorf("contentID is empty") + } + + if devbox.Status.CommitRecords == nil || devbox.Status.CommitRecords[contentID] == nil { + logger.Error(fmt.Errorf("commit record is empty"), "devbox", devbox.Name, "contentID", contentID) + return fmt.Errorf("commit record is empty") + } + + currentRecord := devbox.Status.CommitRecords[contentID] + baseImage := currentRecord.BaseImage + if baseImage == "" { + logger.Error(fmt.Errorf("baseImage is empty"), "devbox", devbox.Name, "contentID", contentID) + return fmt.Errorf("baseImage is empty") + } + + logger.Info("Starting devbox deletion storage cleanup", "devbox", devbox.Name, "contentID", devbox.Status.ContentID) + + // use StateChangeRecorder to delete storage + eventMessage := fmt.Sprintf(events.EventMessageStorageCleanupFormat, devbox.Name, contentID, baseImage) + logger.Info("deleting storage", "eventMessage", eventMessage) + r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, events.EventReasonStorageCleanupRequested, eventMessage) + return nil +} + func (r *DevboxReconciler) generateImageName(devbox *devboxv1alpha2.Devbox) string { now := time.Now() return fmt.Sprintf("%s/%s/%s:%s-%s", r.CommitImageRegistry, devbox.Namespace, devbox.Name, rand.String(5), now.Format("2006-01-02-150405")) diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/state_change_handler.go index 907c188f5268..cec595dcfde9 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/state_change_handler.go @@ -3,6 +3,7 @@ package controller import ( "context" "fmt" + "strings" "sync" "time" @@ -10,7 +11,7 @@ import ( "github.com/google/uuid" devboxv1alpha2 "github.com/labring/sealos/controllers/devbox/api/v1alpha2" "github.com/labring/sealos/controllers/devbox/internal/commit" - + "github.com/labring/sealos/controllers/devbox/internal/controller/utils/events" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -48,11 +49,30 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er h.Logger.Info("event source host is not the node name, skip", "event", event) return nil } + + switch event.Reason { + // handle storage cleanup + case events.EventReasonStorageCleanupRequested: + return h.handleStorageCleanup(ctx, event) + + // handle state change + case events.EventReasonDevboxStateChanged: + return h.handleDevboxStateChange(ctx, event) + + default: + return fmt.Errorf("invalid event: %v", event) + } +} + +// handleDevboxStateChange handle new structured state change event +func (h *StateChangeHandler) handleDevboxStateChange(ctx context.Context, event *corev1.Event) error { + h.Logger.Info("Devbox state change event detected", "event", event.Name, "message", event.Message) devbox := &devboxv1alpha2.Devbox{} if err := h.Client.Get(ctx, types.NamespacedName{Namespace: event.Namespace, Name: event.InvolvedObject.Name}, devbox); err != nil { h.Logger.Error(err, "failed to get devbox", "devbox", event.InvolvedObject.Name) return err } + // Check if state transition is valid and handle accordingly currentState := devbox.Status.State targetState := devbox.Spec.State @@ -92,6 +112,28 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er return nil } +func (h *StateChangeHandler) handleStorageCleanup(ctx context.Context, event *corev1.Event) error { + h.Logger.Info("Storage cleanup event detected", "event", event.Name, "message", event.Message) + if err := h.removeStorage(ctx, event); err != nil { + h.Logger.Error(err, "failed to clean up storage during delete devbox", "devbox", event.Name) + h.Recorder.Eventf(&corev1.ObjectReference{ + Kind: event.InvolvedObject.Kind, + Name: event.InvolvedObject.Name, + Namespace: event.InvolvedObject.Namespace, + }, corev1.EventTypeWarning, "Storage cleanup failed", + "Failed to cleanup Storage: %v", err) + } else { + h.Logger.Info("Successfully cleaned up storage during deletion", "devbox", event.Name) + h.Recorder.Eventf(&corev1.ObjectReference{ + Kind: event.InvolvedObject.Kind, + Name: event.InvolvedObject.Name, + Namespace: event.InvolvedObject.Namespace, + }, corev1.EventTypeNormal, "Storage cleanup succeeded", + "Successfully cleaned up storage for devbox %s", event.Name) + } + return nil +} + func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha2.Devbox, targetState devboxv1alpha2.DevboxState) error { defer commitMap.Delete(devbox.Status.ContentID) if err := h.Client.Get(ctx, types.NamespacedName{Namespace: devbox.Namespace, Name: devbox.Name}, devbox); err != nil { @@ -176,9 +218,9 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a h.Logger.Error(err, "failed to update devbox status", "devbox", devbox.Name) return err } - // step 5: set lv removable + // step 5: set LV removable if err := h.Committer.SetLvRemovable(ctx, containerID, oldContentID); err != nil { - h.Logger.Error(err, "failed to set lv removable", "containerID", containerID, "contentID", oldContentID) + h.Logger.Error(err, "failed to set LV removable", "containerID", containerID, "contentID", oldContentID) } return nil } @@ -187,3 +229,91 @@ func (h *StateChangeHandler) generateImageName(devbox *devboxv1alpha2.Devbox) st now := time.Now() return fmt.Sprintf("%s/%s/%s:%s-%s", h.CommitImageRegistry, devbox.Namespace, devbox.Name, rand.String(5), now.Format("2006-01-02-150405")) } + +func (h *StateChangeHandler) removeStorage(ctx context.Context, event *corev1.Event) error { + h.Logger.Info("Starting devbox deletion Storage cleanup", "devbox", event.Name, "message", event.Message) + devboxName, contentID, baseImage, err := h.parseStorageCleanupMessage(event.Message) + if err != nil { + h.Logger.Error(err, "failed to parse Storage cleanup message", "event", event) + return err + } + + const maxRetries = 3 + const retryDelay = 2 * time.Second + + for i := 0; i < maxRetries; i++ { + if err := h.cleanupStorage(ctx, devboxName, contentID, baseImage); err == nil { + h.Logger.Info("Successfully completed Storage cleanup", "devbox", devboxName, "attempt", i+1) + return nil + } else { + h.Logger.Error(err, "Storage cleanup failed, retrying...", "devbox", devboxName, "attempt", i+1, "maxRetries", maxRetries) + + if i < maxRetries-1 { + time.Sleep(retryDelay) + } + } + } + + return fmt.Errorf("failed to cleanup Storage after %d attempts", maxRetries) +} + +func (h *StateChangeHandler) cleanupStorage(ctx context.Context, devboxName, contentID, baseImage string) error { + h.Logger.Info("Starting Storage cleanup", "devbox", devboxName, "contentID", contentID, "baseImage", baseImage) + + // create temp container + containerID, err := h.Committer.CreateContainer(ctx, fmt.Sprintf("temp-%s-%d", devboxName, time.Now().UnixMicro()), contentID, baseImage) + if err != nil { + h.Logger.Error(err, "failed to create temp container", "devbox", devboxName, "contentID", contentID, "baseImage", baseImage) + return err + } + + // make sure remove container + defer func() { + if cleanupErr := h.Committer.RemoveContainer(ctx, containerID); cleanupErr != nil { + h.Logger.Error(cleanupErr, "failed to remove temporary container", "devbox", devboxName, "containerID", containerID) + } else { + h.Logger.Info("Successfully removed temporary container", "devbox", devboxName, "containerID", containerID) + } + }() + + // remove storage + if err := h.Committer.SetLvRemovable(ctx, containerID, contentID); err != nil { + h.Logger.Error(err, "failed to set Storage removable", "devbox", devboxName, "containerID", containerID, "contentID", contentID) + return fmt.Errorf("failed to set Storage removable: %w", err) + } + + h.Logger.Info("Successfully completed Storage cleanup", "devbox", devboxName, "containerID", containerID, "contentID", contentID) + + return nil +} + +// parseStorageCleanupMessage parses the message from the event and returns the devboxName, contentID, and baseImage +func (h *StateChangeHandler) parseStorageCleanupMessage(message string) (devboxName, contentID, baseImage string, err error) { + parts := strings.Split(message, ", ") + if len(parts) != 3 { + return "", "", "", fmt.Errorf("invalid message format: %s", message) + } + + // resolve devboxName + devboxNamePart := parts[0] + if !strings.Contains(devboxNamePart, "devboxName=") { + return "", "", "", fmt.Errorf("missing devboxName in message: %s", message) + } + devboxName = strings.TrimPrefix(devboxNamePart, "devboxName=") + + // contentID + contentIDPart := parts[1] + if !strings.Contains(contentIDPart, "contentID=") { + return "", "", "", fmt.Errorf("missing contentID in message: %s", message) + } + contentID = strings.TrimPrefix(contentIDPart, "contentID=") + + // baseImage + baseImagePart := parts[2] + if !strings.Contains(baseImagePart, "baseImage=") { + return "", "", "", fmt.Errorf("missing baseImage in message: %s", message) + } + baseImage = strings.TrimPrefix(baseImagePart, "baseImage=") + + return devboxName, contentID, baseImage, nil +} diff --git a/controllers/devbox/internal/controller/utils/events/const.go b/controllers/devbox/internal/controller/utils/events/const.go new file mode 100644 index 000000000000..f44987cdbe8e --- /dev/null +++ b/controllers/devbox/internal/controller/utils/events/const.go @@ -0,0 +1,8 @@ +package events + +const ( + EventReasonStorageCleanupRequested = "Storage cleanup requested" // Storage cleanup events + EventReasonDevboxStateChanged = "Devbox state changed" // Devbox state changed events + + EventMessageStorageCleanupFormat = "devboxName=%s, contentID=%s, baseImage=%s" +) From 8a76747aef6eea41fdaa9d5dfb7bac9e412552d7 Mon Sep 17 00:00:00 2001 From: yy <56745951+lingdie@users.noreply.github.com> Date: Wed, 10 Sep 2025 15:49:24 +0800 Subject: [PATCH 39/46] fix devboxrelease type (#65) --- controllers/devbox/Makefile | 2 +- controllers/devbox/PROJECT | 4 +- .../api/v1alpha2/devboxrelease_types.go | 40 ++++--- .../api/v1alpha2/zz_generated.deepcopy.go | 111 +++++++++--------- .../crd/bases/devbox.sealos.io_devboxes.yaml | 2 +- .../devbox.sealos.io_devboxreleases.yaml | 13 +- .../controller/devboxrelease_controller.go | 14 +-- .../devboxrelease_controller_test.go | 6 +- 8 files changed, 99 insertions(+), 93 deletions(-) diff --git a/controllers/devbox/Makefile b/controllers/devbox/Makefile index 7fcfb46f7e46..63d2b026eb8a 100644 --- a/controllers/devbox/Makefile +++ b/controllers/devbox/Makefile @@ -127,7 +127,7 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions KUSTOMIZE_VERSION ?= v5.3.0 -CONTROLLER_TOOLS_VERSION ?= v0.16.1 +CONTROLLER_TOOLS_VERSION ?= v0.18.0 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize diff --git a/controllers/devbox/PROJECT b/controllers/devbox/PROJECT index 1e9e3bb1c399..91e93db0c76f 100644 --- a/controllers/devbox/PROJECT +++ b/controllers/devbox/PROJECT @@ -23,7 +23,7 @@ resources: controller: true domain: sealos.io group: devbox - kind: Devboxreleases + kind: DevBoxReleases path: github.com/labring/sealos/controllers/devbox/api/v1alpha2 version: v1alpha2 - api: @@ -32,7 +32,7 @@ resources: controller: true domain: sealos.io group: devbox - kind: DevboxRelease + kind: DevBoxRelease path: github.com/labring/sealos/controllers/devbox/api/v1alpha2 version: v1alpha2 version: "3" diff --git a/controllers/devbox/api/v1alpha2/devboxrelease_types.go b/controllers/devbox/api/v1alpha2/devboxrelease_types.go index 9a985acf6d1f..3a4467aae3e7 100644 --- a/controllers/devbox/api/v1alpha2/devboxrelease_types.go +++ b/controllers/devbox/api/v1alpha2/devboxrelease_types.go @@ -20,8 +20,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// DevboxReleaseSpec defines the desired state of Devboxrelease. -type DevboxReleaseSpec struct { +// DevBoxReleaseSpec defines the desired state of Devboxrelease. +type DevBoxReleaseSpec struct { // +kubebuilder:validation:Required DevboxName string `json:"devboxName"` // +kubebuilder:validation:Required @@ -30,51 +30,53 @@ type DevboxReleaseSpec struct { Notes string `json:"notes,omitempty"` } -type DevboxReleasePhase string +type DevBoxReleasePhase string const ( - // DevboxReleasePhaseSuccess means the Devbox has been released - DevboxReleasePhaseSuccess DevboxReleasePhase = "Success" - // DevboxReleasePhasePending means the Devbox has not been released - DevboxReleasePhasePending DevboxReleasePhase = "Pending" - // DevboxReleasePhaseFailed means the Devbox has not been released - DevboxReleasePhaseFailed DevboxReleasePhase = "Failed" + // DevBoxReleasePhaseSuccess means the Devbox has been released + DevBoxReleasePhaseSuccess DevBoxReleasePhase = "Success" + // DevBoxReleasePhasePending means the Devbox has not been released + DevBoxReleasePhasePending DevBoxReleasePhase = "Pending" + // DevBoxReleasePhaseFailed means the Devbox has not been released + DevBoxReleasePhaseFailed DevBoxReleasePhase = "Failed" ) -type DevboxReleaseStatus struct { +type DevBoxReleaseStatus struct { // +kubebuilder:validation:Optional // +kubebuilder:default=Pending // +kubebuilder:validation:Enum=Success;Pending;Failed - Phase DevboxReleasePhase `json:"phase,omitempty"` + Phase DevBoxReleasePhase `json:"phase,omitempty"` OriginalDevboxState DevboxState `json:"originalDevboxState,omitempty"` SourceImage string `json:"sourceImage,omitempty"` TargetImage string `json:"targetImage,omitempty"` } // +kubebuilder:object:root=true +// +kubebuilder:storageversion // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" +// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.version" // +kubebuilder:printcolumn:name="SourceImage",type="string",JSONPath=".status.sourceImage" // +kubebuilder:printcolumn:name="TargetImage",type="string",JSONPath=".status.targetImage" -// DevboxRelease is the Schema for the devboxreleases API. -type DevboxRelease struct { +// DevBoxRelease is the Schema for the devboxreleases API. +type DevBoxRelease struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec DevboxReleaseSpec `json:"spec,omitempty"` - Status DevboxReleaseStatus `json:"status,omitempty"` + Spec DevBoxReleaseSpec `json:"spec,omitempty"` + Status DevBoxReleaseStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true -// DevboxReleaseList contains a list of DevboxRelease. -type DevboxReleaseList struct { +// DevBoxReleaseList contains a list of DevBoxRelease. +type DevBoxReleaseList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []DevboxRelease `json:"items"` + Items []DevBoxRelease `json:"items"` } func init() { - SchemeBuilder.Register(&DevboxRelease{}, &DevboxReleaseList{}) + SchemeBuilder.Register(&DevBoxRelease{}, &DevBoxReleaseList{}) } diff --git a/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go index b9ebc76bae4e..4cce66e21898 100644 --- a/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go +++ b/controllers/devbox/api/v1alpha2/zz_generated.deepcopy.go @@ -157,26 +157,26 @@ func (in *Config) DeepCopy() *Config { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Devbox) DeepCopyInto(out *Devbox) { +func (in *DevBoxRelease) DeepCopyInto(out *DevBoxRelease) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) + out.Spec = in.Spec + out.Status = in.Status } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Devbox. -func (in *Devbox) DeepCopy() *Devbox { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevBoxRelease. +func (in *DevBoxRelease) DeepCopy() *DevBoxRelease { if in == nil { return nil } - out := new(Devbox) + out := new(DevBoxRelease) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Devbox) DeepCopyObject() runtime.Object { +func (in *DevBoxRelease) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -184,31 +184,31 @@ func (in *Devbox) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevboxList) DeepCopyInto(out *DevboxList) { +func (in *DevBoxReleaseList) DeepCopyInto(out *DevBoxReleaseList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]Devbox, len(*in)) + *out = make([]DevBoxRelease, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxList. -func (in *DevboxList) DeepCopy() *DevboxList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevBoxReleaseList. +func (in *DevBoxReleaseList) DeepCopy() *DevBoxReleaseList { if in == nil { return nil } - out := new(DevboxList) + out := new(DevBoxReleaseList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DevboxList) DeepCopyObject() runtime.Object { +func (in *DevBoxReleaseList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -216,58 +216,56 @@ func (in *DevboxList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevboxRelease) DeepCopyInto(out *DevboxRelease) { +func (in *DevBoxReleaseSpec) DeepCopyInto(out *DevBoxReleaseSpec) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxRelease. -func (in *DevboxRelease) DeepCopy() *DevboxRelease { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevBoxReleaseSpec. +func (in *DevBoxReleaseSpec) DeepCopy() *DevBoxReleaseSpec { if in == nil { return nil } - out := new(DevboxRelease) + out := new(DevBoxReleaseSpec) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DevboxRelease) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevBoxReleaseStatus) DeepCopyInto(out *DevBoxReleaseStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevBoxReleaseStatus. +func (in *DevBoxReleaseStatus) DeepCopy() *DevBoxReleaseStatus { + if in == nil { + return nil } - return nil + out := new(DevBoxReleaseStatus) + in.DeepCopyInto(out) + return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevboxReleaseList) DeepCopyInto(out *DevboxReleaseList) { +func (in *Devbox) DeepCopyInto(out *Devbox) { *out = *in out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]DevboxRelease, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxReleaseList. -func (in *DevboxReleaseList) DeepCopy() *DevboxReleaseList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Devbox. +func (in *Devbox) DeepCopy() *Devbox { if in == nil { return nil } - out := new(DevboxReleaseList) + out := new(Devbox) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DevboxReleaseList) DeepCopyObject() runtime.Object { +func (in *Devbox) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -275,33 +273,35 @@ func (in *DevboxReleaseList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevboxReleaseSpec) DeepCopyInto(out *DevboxReleaseSpec) { +func (in *DevboxList) DeepCopyInto(out *DevboxList) { *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Devbox, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxReleaseSpec. -func (in *DevboxReleaseSpec) DeepCopy() *DevboxReleaseSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxList. +func (in *DevboxList) DeepCopy() *DevboxList { if in == nil { return nil } - out := new(DevboxReleaseSpec) + out := new(DevboxList) in.DeepCopyInto(out) return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DevboxReleaseStatus) DeepCopyInto(out *DevboxReleaseStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxReleaseStatus. -func (in *DevboxReleaseStatus) DeepCopy() *DevboxReleaseStatus { - if in == nil { - return nil +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DevboxList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c } - out := new(DevboxReleaseStatus) - in.DeepCopyInto(out) - return out + return nil } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -367,6 +367,7 @@ func (in *DevboxStatus) DeepCopyInto(out *DevboxStatus) { } } out.Network = in.Network + in.LastContainerStatus.DeepCopyInto(&out.LastContainerStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevboxStatus. diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index d8c8cf44f281..ee01ffee7c40 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.18.0 name: devboxes.devbox.sealos.io spec: group: devbox.sealos.io diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml index f211be9112d1..4f38e149ef70 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml @@ -3,13 +3,13 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.18.0 name: devboxreleases.devbox.sealos.io spec: group: devbox.sealos.io names: - kind: DevboxRelease - listKind: DevboxReleaseList + kind: DevBoxRelease + listKind: DevBoxReleaseList plural: devboxreleases singular: devboxrelease scope: Namespaced @@ -18,6 +18,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .spec.version + name: Version + type: string - jsonPath: .status.sourceImage name: SourceImage type: string @@ -27,7 +30,7 @@ spec: name: v1alpha2 schema: openAPIV3Schema: - description: DevboxRelease is the Schema for the devboxreleases API. + description: DevBoxRelease is the Schema for the devboxreleases API. properties: apiVersion: description: |- @@ -47,7 +50,7 @@ spec: metadata: type: object spec: - description: DevboxReleaseSpec defines the desired state of Devboxrelease. + description: DevBoxReleaseSpec defines the desired state of Devboxrelease. properties: devboxName: type: string diff --git a/controllers/devbox/internal/controller/devboxrelease_controller.go b/controllers/devbox/internal/controller/devboxrelease_controller.go index c127ddde6e7f..5b5a4699d672 100644 --- a/controllers/devbox/internal/controller/devboxrelease_controller.go +++ b/controllers/devbox/internal/controller/devboxrelease_controller.go @@ -45,7 +45,7 @@ type DevboxreleaseReconciler struct { func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) - devboxRelease := &devboxv1alpha2.DevboxRelease{} + devboxRelease := &devboxv1alpha2.DevBoxRelease{} if err := r.Client.Get(ctx, req.NamespacedName, devboxRelease); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } @@ -79,7 +79,7 @@ func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reques return ctrl.Result{RequeueAfter: time.Second * 10}, nil } if devboxRelease.Status.Phase == "" { - devboxRelease.Status.Phase = devboxv1alpha2.DevboxReleasePhasePending + devboxRelease.Status.Phase = devboxv1alpha2.DevBoxReleasePhasePending devboxRelease.Status.OriginalDevboxState = devbox.Spec.State devboxRelease.Status.SourceImage = devbox.Status.CommitRecords[devbox.Status.ContentID].BaseImage devboxRelease.Status.TargetImage = fmt.Sprintf("%s/%s/%s:%s", r.Registry.Host, devboxRelease.Namespace, devboxRelease.Spec.DevboxName, devboxRelease.Spec.Version) @@ -89,7 +89,7 @@ func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reques } return ctrl.Result{Requeue: true}, nil } - if devboxRelease.Status.Phase == devboxv1alpha2.DevboxReleasePhasePending { + if devboxRelease.Status.Phase == devboxv1alpha2.DevBoxReleasePhasePending { logger.Info("Creating release tag", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) err := r.Release(ctx, devboxRelease) if err != nil && errors.Is(err, registry.ErrorManifestNotFound) { @@ -97,12 +97,12 @@ func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reques return ctrl.Result{RequeueAfter: time.Second * 10}, nil } else if err != nil { logger.Error(err, "Failed to create release tag", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) - devboxRelease.Status.Phase = devboxv1alpha2.DevboxReleasePhaseFailed + devboxRelease.Status.Phase = devboxv1alpha2.DevBoxReleasePhaseFailed _ = r.Status().Update(ctx, devboxRelease) return ctrl.Result{}, err } logger.Info("Release tag created", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) - devboxRelease.Status.Phase = devboxv1alpha2.DevboxReleasePhaseSuccess + devboxRelease.Status.Phase = devboxv1alpha2.DevBoxReleasePhaseSuccess if err = r.Status().Update(ctx, devboxRelease); err != nil { logger.Error(err, "Failed to update status", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) return ctrl.Result{}, err @@ -112,7 +112,7 @@ func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reques return ctrl.Result{}, nil } -func (r *DevboxreleaseReconciler) Release(ctx context.Context, devboxRelease *devboxv1alpha2.DevboxRelease) error { +func (r *DevboxreleaseReconciler) Release(ctx context.Context, devboxRelease *devboxv1alpha2.DevBoxRelease) error { logger := log.FromContext(ctx) if err := r.Registry.ReTag(devboxRelease.Status.SourceImage, devboxRelease.Status.TargetImage); err != nil { logger.Error(err, "Failed to re-tag image", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) @@ -125,7 +125,7 @@ func (r *DevboxreleaseReconciler) Release(ctx context.Context, devboxRelease *de // SetupWithManager sets up the controller with the Manager. func (r *DevboxreleaseReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&devboxv1alpha2.DevboxRelease{}). + For(&devboxv1alpha2.DevBoxRelease{}). Named("devboxrelease"). Complete(r) } diff --git a/controllers/devbox/internal/controller/devboxrelease_controller_test.go b/controllers/devbox/internal/controller/devboxrelease_controller_test.go index 1754e49cfeb5..bb8312969915 100644 --- a/controllers/devbox/internal/controller/devboxrelease_controller_test.go +++ b/controllers/devbox/internal/controller/devboxrelease_controller_test.go @@ -40,13 +40,13 @@ var _ = Describe("Devboxrelease Controller", func() { Name: resourceName, Namespace: "default", // TODO(user):Modify as needed } - devboxrelease := &devboxv1alpha2.DevboxRelease{} + devboxrelease := &devboxv1alpha2.DevBoxRelease{} BeforeEach(func() { By("creating the custom resource for the Kind Devboxrelease") err := k8sClient.Get(ctx, typeNamespacedName, devboxrelease) if err != nil && errors.IsNotFound(err) { - resource := &devboxv1alpha2.DevboxRelease{ + resource := &devboxv1alpha2.DevBoxRelease{ ObjectMeta: metav1.ObjectMeta{ Name: resourceName, Namespace: "default", @@ -59,7 +59,7 @@ var _ = Describe("Devboxrelease Controller", func() { AfterEach(func() { // TODO(user): Cleanup logic after each test, like removing the resource instance. - resource := &devboxv1alpha2.DevboxRelease{} + resource := &devboxv1alpha2.DevBoxRelease{} err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) From 0bdc3082b130fb9e44a9477c8d058f28d4b3a131 Mon Sep 17 00:00:00 2001 From: yy <56745951+lingdie@users.noreply.github.com> Date: Fri, 12 Sep 2025 14:52:08 +0800 Subject: [PATCH 40/46] improve devbox event handler. (#66) * improve controller and state change handler * add validation --- .../devbox/api/v1alpha2/devbox_types.go | 1 + controllers/devbox/cmd/main.go | 2 +- .../crd/bases/devbox.sealos.io_devboxes.yaml | 4 + .../internal/controller/devbox_controller.go | 94 +++++++++++++----- ...ate_change_handler.go => event_handler.go} | 97 +++++++------------ .../internal/controller/utils/events/const.go | 20 +++- 6 files changed, 127 insertions(+), 91 deletions(-) rename controllers/devbox/internal/controller/{state_change_handler.go => event_handler.go} (80%) diff --git a/controllers/devbox/api/v1alpha2/devbox_types.go b/controllers/devbox/api/v1alpha2/devbox_types.go index 61fe9ca57a2b..5888acc38832 100644 --- a/controllers/devbox/api/v1alpha2/devbox_types.go +++ b/controllers/devbox/api/v1alpha2/devbox_types.go @@ -274,6 +274,7 @@ type DevboxStatus struct { // Devbox is the Schema for the devboxes API type Devbox struct { + // +kubebuilder:validation:XValidation:rule="self.spec.state == oldSelf.spec.state || (self.status.contentID in self.status.commitRecords && self.status.commitRecords[self.status.contentID].commitStatus != 'Committing')" metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 4d3d9dea4582..26a76b9affda 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -289,7 +289,7 @@ func main() { os.Exit(1) } - stateChangeHandler := controller.StateChangeHandler{ + stateChangeHandler := controller.EventHandler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("state-change-handler"), diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index ee01ffee7c40..e4c0544e9c57 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -3679,6 +3679,10 @@ spec: - commitRecords type: object type: object + x-kubernetes-validations: + - rule: self.spec.state == oldSelf.spec.state || (self.status.contentID in + self.status.commitRecords && self.status.commitRecords[self.status.contentID].commitStatus + != 'Committing') served: true storage: true subresources: diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 3a30d4199ccf..18e58bd65a64 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -120,12 +120,12 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } } else { logger.Info("devbox deleted, remove all resources") - if err := r.removeAll(ctx, devbox, recLabels); err != nil { + if err := r.handleSubResourceDelete(ctx, devbox, recLabels); err != nil { return ctrl.Result{}, err } // delete storage: - if err := r.handleStorageDeleted(ctx, devbox); err != nil { + if err := r.handleStorageDelete(ctx, devbox); err != nil { return ctrl.Result{}, err } @@ -233,8 +233,8 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr logger.Info("devbox state changed, wait for state change handler to handle the event, requeue after 5 seconds", "from", devbox.Status.State, "to", devbox.Spec.State) logger.Info("recording state change event", "devbox", devbox.Name, "nodeName", r.NodeName) - r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, events.EventReasonDevboxStateChanged, "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) - r.Recorder.Eventf(devbox, corev1.EventTypeNormal, events.EventReasonDevboxStateChanged, "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) + r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, events.ReasonDevboxStateChanged, "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) + r.Recorder.Eventf(devbox, corev1.EventTypeNormal, events.ReasonDevboxStateChanged, "Devbox state changed from %s to %s", devbox.Status.State, devbox.Spec.State) return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } @@ -614,44 +614,86 @@ func (r *DevboxReconciler) handlePodDeleted(ctx context.Context, devbox *devboxv return nil } -func (r *DevboxReconciler) handleStorageDeleted(ctx context.Context, devbox *devboxv1alpha2.Devbox) error { +func (r *DevboxReconciler) handleStorageDelete(ctx context.Context, devbox *devboxv1alpha2.Devbox) error { logger := log.FromContext(ctx) - // check status - if devbox.Spec.State == devboxv1alpha2.DevboxStateShutdown || devbox.Spec.State == devboxv1alpha2.DevboxStateStopped { - logger.Info("devbox's spec state is stopped or shutdown, storage already cleaned up", "devbox", devbox.Name, "devboxSpecState", devbox.Spec.State) + // Early return if storage is already cleaned up + if r.isStorageAlreadyCleanedUp(devbox) { + logger.Info("devbox storage already cleaned up, skipping cleanup", + "devbox", devbox.Name, + "state", devbox.Spec.State) return nil } - if devbox.Status.CommitRecords[devbox.Status.ContentID].Node != r.NodeName { - logger.Info("devbox's commit record node is not the same as the node name, skipping storage cleanup", "devbox", devbox.Name, "commitRecordNode", devbox.Status.CommitRecords[devbox.Status.ContentID].Node, "nodeName", r.NodeName) + // Validate and get commit record + commitRecord, err := r.validateAndGetCommitRecord(devbox) + if err != nil { + logger.Error(err, "failed to validate commit record", "devbox", devbox.Name) + return err + } + // Check if this node should handle the cleanup + if !r.shouldHandleStorageCleanup(devbox, commitRecord) { + logger.Info("skipping storage cleanup - not responsible node", + "devbox", devbox.Name, + "commitRecordNode", commitRecord.Node, + "currentNode", r.NodeName) return nil } + // Request storage cleanup + return r.requestStorageCleanup(ctx, devbox, commitRecord) +} + +// isStorageAlreadyCleanedUp checks if storage cleanup is already done +// shutdown or stopped devbox is already cleaned up +func (r *DevboxReconciler) isStorageAlreadyCleanedUp(devbox *devboxv1alpha2.Devbox) bool { + return devbox.Spec.State == devboxv1alpha2.DevboxStateShutdown || + devbox.Spec.State == devboxv1alpha2.DevboxStateStopped +} + +// validateAndGetCommitRecord validates devbox status and returns the current commit record +func (r *DevboxReconciler) validateAndGetCommitRecord(devbox *devboxv1alpha2.Devbox) (*devboxv1alpha2.CommitRecord, error) { contentID := devbox.Status.ContentID if contentID == "" { - logger.Error(fmt.Errorf("contentID is empty"), "devbox", devbox.Name, "devboxSpecState", devbox.Spec.State) - return fmt.Errorf("contentID is empty") + return nil, fmt.Errorf("contentID is empty for devbox %s", devbox.Name) + } + + if devbox.Status.CommitRecords == nil { + return nil, fmt.Errorf("commit records is nil for devbox %s", devbox.Name) } - if devbox.Status.CommitRecords == nil || devbox.Status.CommitRecords[contentID] == nil { - logger.Error(fmt.Errorf("commit record is empty"), "devbox", devbox.Name, "contentID", contentID) - return fmt.Errorf("commit record is empty") + commitRecord, exists := devbox.Status.CommitRecords[contentID] + if !exists || commitRecord == nil { + return nil, fmt.Errorf("commit record not found for contentID %s in devbox %s", contentID, devbox.Name) } - currentRecord := devbox.Status.CommitRecords[contentID] - baseImage := currentRecord.BaseImage - if baseImage == "" { - logger.Error(fmt.Errorf("baseImage is empty"), "devbox", devbox.Name, "contentID", contentID) - return fmt.Errorf("baseImage is empty") + if commitRecord.BaseImage == "" { + return nil, fmt.Errorf("baseImage is empty in commit record for devbox %s", devbox.Name) } - logger.Info("Starting devbox deletion storage cleanup", "devbox", devbox.Name, "contentID", devbox.Status.ContentID) + return commitRecord, nil +} + +// shouldHandleStorageCleanup determines if the current node should handle storage cleanup +func (r *DevboxReconciler) shouldHandleStorageCleanup(devbox *devboxv1alpha2.Devbox, commitRecord *devboxv1alpha2.CommitRecord) bool { + return commitRecord.Node == r.NodeName +} + +// requestStorageCleanup sends a storage cleanup request via event recorder +func (r *DevboxReconciler) requestStorageCleanup(ctx context.Context, devbox *devboxv1alpha2.Devbox, commitRecord *devboxv1alpha2.CommitRecord) error { + logger := log.FromContext(ctx) + + logger.Info("requesting devbox storage cleanup", + "devbox", devbox.Name, + "contentID", devbox.Status.ContentID, + "baseImage", commitRecord.BaseImage) + + r.StateChangeRecorder.AnnotatedEventf(devbox, + events.BuildStorageCleanupAnnotations(devbox.Name, devbox.Status.ContentID, commitRecord.BaseImage), + corev1.EventTypeNormal, + events.ReasonStorageCleanupRequested, + "devbox storage cleanup requested") - // use StateChangeRecorder to delete storage - eventMessage := fmt.Sprintf(events.EventMessageStorageCleanupFormat, devbox.Name, contentID, baseImage) - logger.Info("deleting storage", "eventMessage", eventMessage) - r.StateChangeRecorder.Eventf(devbox, corev1.EventTypeNormal, events.EventReasonStorageCleanupRequested, eventMessage) return nil } @@ -660,7 +702,7 @@ func (r *DevboxReconciler) generateImageName(devbox *devboxv1alpha2.Devbox) stri return fmt.Sprintf("%s/%s/%s:%s-%s", r.CommitImageRegistry, devbox.Namespace, devbox.Name, rand.String(5), now.Format("2006-01-02-150405")) } -func (r *DevboxReconciler) removeAll(ctx context.Context, devbox *devboxv1alpha2.Devbox, recLabels map[string]string) error { +func (r *DevboxReconciler) handleSubResourceDelete(ctx context.Context, devbox *devboxv1alpha2.Devbox, recLabels map[string]string) error { // Delete Pod podList := &corev1.PodList{} if err := r.List(ctx, podList, client.InNamespace(devbox.Namespace), client.MatchingLabels(recLabels)); err != nil { diff --git a/controllers/devbox/internal/controller/state_change_handler.go b/controllers/devbox/internal/controller/event_handler.go similarity index 80% rename from controllers/devbox/internal/controller/state_change_handler.go rename to controllers/devbox/internal/controller/event_handler.go index cec595dcfde9..79f7bb806221 100644 --- a/controllers/devbox/internal/controller/state_change_handler.go +++ b/controllers/devbox/internal/controller/event_handler.go @@ -3,7 +3,6 @@ package controller import ( "context" "fmt" - "strings" "sync" "time" @@ -17,6 +16,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" @@ -24,7 +24,7 @@ import ( var commitMap = sync.Map{} -type StateChangeHandler struct { +type EventHandler struct { Committer commit.Committer CommitImageRegistry string NodeName string @@ -36,7 +36,7 @@ type StateChangeHandler struct { } // todo: handle state change event -func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) error { +func (h *EventHandler) Handle(ctx context.Context, event *corev1.Event) error { h.Logger.Info("StateChangeHandler.Handle called", "event", event.Name, "eventSourceHost", event.Source.Host, @@ -52,11 +52,11 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er switch event.Reason { // handle storage cleanup - case events.EventReasonStorageCleanupRequested: + case events.ReasonStorageCleanupRequested: return h.handleStorageCleanup(ctx, event) // handle state change - case events.EventReasonDevboxStateChanged: + case events.ReasonDevboxStateChanged: return h.handleDevboxStateChange(ctx, event) default: @@ -65,7 +65,7 @@ func (h *StateChangeHandler) Handle(ctx context.Context, event *corev1.Event) er } // handleDevboxStateChange handle new structured state change event -func (h *StateChangeHandler) handleDevboxStateChange(ctx context.Context, event *corev1.Event) error { +func (h *EventHandler) handleDevboxStateChange(ctx context.Context, event *corev1.Event) error { h.Logger.Info("Devbox state change event detected", "event", event.Name, "message", event.Message) devbox := &devboxv1alpha2.Devbox{} if err := h.Client.Get(ctx, types.NamespacedName{Namespace: event.Namespace, Name: event.InvolvedObject.Name}, devbox); err != nil { @@ -112,7 +112,7 @@ func (h *StateChangeHandler) handleDevboxStateChange(ctx context.Context, event return nil } -func (h *StateChangeHandler) handleStorageCleanup(ctx context.Context, event *corev1.Event) error { +func (h *EventHandler) handleStorageCleanup(ctx context.Context, event *corev1.Event) error { h.Logger.Info("Storage cleanup event detected", "event", event.Name, "message", event.Message) if err := h.removeStorage(ctx, event); err != nil { h.Logger.Error(err, "failed to clean up storage during delete devbox", "devbox", event.Name) @@ -134,7 +134,7 @@ func (h *StateChangeHandler) handleStorageCleanup(ctx context.Context, event *co return nil } -func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha2.Devbox, targetState devboxv1alpha2.DevboxState) error { +func (h *EventHandler) commitDevbox(ctx context.Context, devbox *devboxv1alpha2.Devbox, targetState devboxv1alpha2.DevboxState) error { defer commitMap.Delete(devbox.Status.ContentID) if err := h.Client.Get(ctx, types.NamespacedName{Namespace: devbox.Namespace, Name: devbox.Name}, devbox); err != nil { h.Logger.Error(err, "failed to get devbox", "devbox", devbox.Name) @@ -225,39 +225,37 @@ func (h *StateChangeHandler) commitDevbox(ctx context.Context, devbox *devboxv1a return nil } -func (h *StateChangeHandler) generateImageName(devbox *devboxv1alpha2.Devbox) string { +func (h *EventHandler) generateImageName(devbox *devboxv1alpha2.Devbox) string { now := time.Now() return fmt.Sprintf("%s/%s/%s:%s-%s", h.CommitImageRegistry, devbox.Namespace, devbox.Name, rand.String(5), now.Format("2006-01-02-150405")) } -func (h *StateChangeHandler) removeStorage(ctx context.Context, event *corev1.Event) error { +func (h *EventHandler) removeStorage(ctx context.Context, event *corev1.Event) error { h.Logger.Info("Starting devbox deletion Storage cleanup", "devbox", event.Name, "message", event.Message) - devboxName, contentID, baseImage, err := h.parseStorageCleanupMessage(event.Message) + devboxName, contentID, baseImage := h.parseStorageCleanupAnno(event.Annotations) + + // Use k8s.io/client-go/util/retry for robust retry logic + err := retry.OnError( + wait.Backoff{ + Steps: 3, + Duration: 2 * time.Second, + Factor: 1.0, + Jitter: 0.1, + }, + func(err error) bool { return true }, + func() error { + return h.cleanupStorage(ctx, devboxName, contentID, baseImage) + }, + ) if err != nil { - h.Logger.Error(err, "failed to parse Storage cleanup message", "event", event) - return err + h.Logger.Error(err, "Failed to cleanup storage after all retries", "devbox", devboxName) + return fmt.Errorf("failed to cleanup storage for devbox %s after retries: %w", devboxName, err) } - - const maxRetries = 3 - const retryDelay = 2 * time.Second - - for i := 0; i < maxRetries; i++ { - if err := h.cleanupStorage(ctx, devboxName, contentID, baseImage); err == nil { - h.Logger.Info("Successfully completed Storage cleanup", "devbox", devboxName, "attempt", i+1) - return nil - } else { - h.Logger.Error(err, "Storage cleanup failed, retrying...", "devbox", devboxName, "attempt", i+1, "maxRetries", maxRetries) - - if i < maxRetries-1 { - time.Sleep(retryDelay) - } - } - } - - return fmt.Errorf("failed to cleanup Storage after %d attempts", maxRetries) + h.Logger.Info("Successfully completed storage cleanup", "devbox", devboxName) + return nil } -func (h *StateChangeHandler) cleanupStorage(ctx context.Context, devboxName, contentID, baseImage string) error { +func (h *EventHandler) cleanupStorage(ctx context.Context, devboxName, contentID, baseImage string) error { h.Logger.Info("Starting Storage cleanup", "devbox", devboxName, "contentID", contentID, "baseImage", baseImage) // create temp container @@ -287,33 +285,10 @@ func (h *StateChangeHandler) cleanupStorage(ctx context.Context, devboxName, con return nil } -// parseStorageCleanupMessage parses the message from the event and returns the devboxName, contentID, and baseImage -func (h *StateChangeHandler) parseStorageCleanupMessage(message string) (devboxName, contentID, baseImage string, err error) { - parts := strings.Split(message, ", ") - if len(parts) != 3 { - return "", "", "", fmt.Errorf("invalid message format: %s", message) - } - - // resolve devboxName - devboxNamePart := parts[0] - if !strings.Contains(devboxNamePart, "devboxName=") { - return "", "", "", fmt.Errorf("missing devboxName in message: %s", message) - } - devboxName = strings.TrimPrefix(devboxNamePart, "devboxName=") - - // contentID - contentIDPart := parts[1] - if !strings.Contains(contentIDPart, "contentID=") { - return "", "", "", fmt.Errorf("missing contentID in message: %s", message) - } - contentID = strings.TrimPrefix(contentIDPart, "contentID=") - - // baseImage - baseImagePart := parts[2] - if !strings.Contains(baseImagePart, "baseImage=") { - return "", "", "", fmt.Errorf("missing baseImage in message: %s", message) - } - baseImage = strings.TrimPrefix(baseImagePart, "baseImage=") - - return devboxName, contentID, baseImage, nil +// parseStorageCleanupAnno parses the annotations from the event and returns the devboxName, contentID, and baseImage +func (h *EventHandler) parseStorageCleanupAnno(annotations events.Annotations) (devboxName, contentID, baseImage string) { + devboxName = annotations[events.KeyAnnotationDevboxName] + contentID = annotations[events.KeyAnnotationContentID] + baseImage = annotations[events.KeyAnnotationBaseImage] + return devboxName, contentID, baseImage } diff --git a/controllers/devbox/internal/controller/utils/events/const.go b/controllers/devbox/internal/controller/utils/events/const.go index f44987cdbe8e..20f1d3836aaa 100644 --- a/controllers/devbox/internal/controller/utils/events/const.go +++ b/controllers/devbox/internal/controller/utils/events/const.go @@ -1,8 +1,22 @@ package events const ( - EventReasonStorageCleanupRequested = "Storage cleanup requested" // Storage cleanup events - EventReasonDevboxStateChanged = "Devbox state changed" // Devbox state changed events + ReasonStorageCleanupRequested = "storage-cleanup-requested" + ReasonDevboxStateChanged = "devbox-state-changed" - EventMessageStorageCleanupFormat = "devboxName=%s, contentID=%s, baseImage=%s" + KeyAnnotationReason = "reason" + KeyAnnotationDevboxName = "devbox-name" + KeyAnnotationContentID = "content-id" + KeyAnnotationBaseImage = "base-image" ) + +type Annotations map[string]string + +func BuildStorageCleanupAnnotations(devboxName, contentID, baseImage string) Annotations { + return Annotations{ + KeyAnnotationReason: ReasonStorageCleanupRequested, + KeyAnnotationDevboxName: devboxName, + KeyAnnotationContentID: contentID, + KeyAnnotationBaseImage: baseImage, + } +} From 461842ed697c22887ec15ecc09a6b8dd1edfd92a Mon Sep 17 00:00:00 2001 From: yy <56745951+lingdie@users.noreply.github.com> Date: Fri, 12 Sep 2025 17:12:15 +0800 Subject: [PATCH 41/46] add start config in devbox release (#67) --- .../api/v1alpha2/devboxrelease_types.go | 4 ++++ .../devbox.sealos.io_devboxreleases.yaml | 6 ++++++ .../controller/devboxrelease_controller.go | 20 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/controllers/devbox/api/v1alpha2/devboxrelease_types.go b/controllers/devbox/api/v1alpha2/devboxrelease_types.go index 3a4467aae3e7..190bade6a794 100644 --- a/controllers/devbox/api/v1alpha2/devboxrelease_types.go +++ b/controllers/devbox/api/v1alpha2/devboxrelease_types.go @@ -28,6 +28,9 @@ type DevBoxReleaseSpec struct { Version string `json:"version"` // +kubebuilder:validation:Optional Notes string `json:"notes,omitempty"` + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + StartDevboxAfterRelease bool `json:"startDevboxAfterRelease,omitempty"` } type DevBoxReleasePhase string @@ -58,6 +61,7 @@ type DevBoxReleaseStatus struct { // +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.version" // +kubebuilder:printcolumn:name="SourceImage",type="string",JSONPath=".status.sourceImage" // +kubebuilder:printcolumn:name="TargetImage",type="string",JSONPath=".status.targetImage" +// +kubebuilder:printcolumn:name="StartDevboxAfterRelease",type="boolean",JSONPath=".spec.startDevboxAfterRelease" // DevBoxRelease is the Schema for the devboxreleases API. type DevBoxRelease struct { diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml index 4f38e149ef70..aa053367949f 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxreleases.yaml @@ -27,6 +27,9 @@ spec: - jsonPath: .status.targetImage name: TargetImage type: string + - jsonPath: .spec.startDevboxAfterRelease + name: StartDevboxAfterRelease + type: boolean name: v1alpha2 schema: openAPIV3Schema: @@ -56,6 +59,9 @@ spec: type: string notes: type: string + startDevboxAfterRelease: + default: false + type: boolean version: type: string required: diff --git a/controllers/devbox/internal/controller/devboxrelease_controller.go b/controllers/devbox/internal/controller/devboxrelease_controller.go index 5b5a4699d672..d2bf4f8a51a7 100644 --- a/controllers/devbox/internal/controller/devboxrelease_controller.go +++ b/controllers/devbox/internal/controller/devboxrelease_controller.go @@ -23,6 +23,7 @@ import ( "time" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/util/retry" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -107,6 +108,25 @@ func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reques logger.Error(err, "Failed to update status", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) return ctrl.Result{}, err } + if devboxRelease.Spec.StartDevboxAfterRelease { + err = retry.RetryOnConflict(retry.DefaultRetry, func() error { + devbox := &devboxv1alpha2.Devbox{} + if err := r.Get(ctx, client.ObjectKey{Namespace: devboxRelease.Namespace, Name: devboxRelease.Spec.DevboxName}, devbox); err != nil { + return err + } + logger.Info("Starting devbox after release", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + devbox.Spec.State = devboxv1alpha2.DevboxStateRunning + if err = r.Update(ctx, devbox); err != nil { + logger.Error(err, "Failed to update devbox", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + return err + } + return nil + }) + if err != nil { + logger.Error(err, "Failed to update devbox", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + return ctrl.Result{}, err + } + } } logger.Info("Reconciliation complete", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) return ctrl.Result{}, nil From a04cc89d7fa417e566be751f56e6dbfa1964c7a0 Mon Sep 17 00:00:00 2001 From: yy <56745951+lingdie@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:10:00 +0800 Subject: [PATCH 42/46] add login action when new committer (#68) --- controllers/devbox/internal/commit/commit.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 2ce55f2d6ed5..1a7133ffbaa3 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -17,6 +17,7 @@ import ( "github.com/containerd/nerdctl/v2/pkg/api/types" "github.com/containerd/nerdctl/v2/pkg/cmd/container" "github.com/containerd/nerdctl/v2/pkg/cmd/image" + "github.com/containerd/nerdctl/v2/pkg/cmd/login" "github.com/containerd/nerdctl/v2/pkg/containerutil" ncdefaults "github.com/containerd/nerdctl/v2/pkg/defaults" @@ -57,6 +58,17 @@ func NewCommitter(registryAddr, registryUsername, registryPassword string) (Comm var conn *grpc.ClientConn var err error + // login to registry + err = login.Login(context.Background(), types.LoginCommandOptions{ + GOptions: *NewGlobalOptionConfig(), + ServerAddress: registryAddr, + Username: registryUsername, + Password: registryPassword, + }, io.Discard) + if err != nil { + return nil, err + } + // retry to connect for i := 0; i <= DefaultMaxRetries; i++ { if i > 0 { From 2ae4f9b618e564063372b55f8ac381be55eefc94 Mon Sep 17 00:00:00 2001 From: Cunzili <140624320+Zllinc@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:26:09 +0800 Subject: [PATCH 43/46] fix(commit): close image layer merge operation (#69) * close merge * add flag to merge --------- Co-authored-by: Cunzili --- controllers/devbox/cmd/main.go | 9 +++-- controllers/devbox/internal/commit/commit.go | 34 ++++++++++++------- .../devbox/internal/commit/commit_test.go | 32 ++++++++--------- .../internal/controller/devbox_controller.go | 12 ++++--- controllers/go.work.sum | 7 ++++ 5 files changed, 59 insertions(+), 35 deletions(-) diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 26a76b9affda..13495e0452d8 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -100,6 +100,8 @@ func main() { // devbox node label var devboxNodeLabel string var acceptanceThreshold int + // merge base image layers flag + var mergeBaseImageTopLayer bool flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") @@ -129,11 +131,13 @@ func main() { flag.IntVar(&configQPS, "config-qps", 50, "The qps of the config") flag.IntVar(&configBurst, "config-burst", 100, "The burst of the config") // config restart predicate duration - flag.DurationVar(&restartPredicateDuration, "restart-predicate-duration", 2*time.Hour, "Sets the restart predicate time duration for devbox controller restart. By default, the duration is set to 2 hours.") + flag.DurationVar(&restartPredicateDuration, "restart-predicate-duration", 10000*time.Hour, "Sets the restart predicate time duration for devbox controller restart. By default, the duration is set to 2 hours.") // devbox node label flag.StringVar(&devboxNodeLabel, "devbox-node-label", "devbox.sealos.io/node", "The label of the devbox node") // scheduling flags flag.IntVar(&acceptanceThreshold, "acceptance-threshold", 16, "The minimum acceptance score for scheduling devbox to node. Default is 16, which means the node must have enough resources to run the devbox.") + // merge base image layers flag + flag.BoolVar(&mergeBaseImageTopLayer, "merge-base-image-top-layer", false, "If set true, devbox will merge base image top layers during create and remove top layer during commit.") opts := zap.Options{ Development: true, } @@ -273,12 +277,13 @@ func main() { NodeName: nodes.GetNodeName(), AcceptanceThreshold: acceptanceThreshold, NodeStatsProvider: &stat.NodeStatsProviderImpl{}, + MergeBaseImageTopLayer: mergeBaseImageTopLayer, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Devbox") os.Exit(1) } - committer, err := commit.NewCommitter(registryAddr, registryUser, registryPassword) + committer, err := commit.NewCommitter(registryAddr, registryUser, registryPassword, mergeBaseImageTopLayer) if err != nil { setupLog.Error(err, "unable to create committer") os.Exit(1) diff --git a/controllers/devbox/internal/commit/commit.go b/controllers/devbox/internal/commit/commit.go index 1a7133ffbaa3..95928a40fe42 100644 --- a/controllers/devbox/internal/commit/commit.go +++ b/controllers/devbox/internal/commit/commit.go @@ -47,6 +47,8 @@ type CommitterImpl struct { registryAddr string registryUsername string registryPassword string + // Merge base image layers control + mergeBaseImageTopLayer bool // GC gcContainerMap map[string]struct{} gcImageMap map[string]struct{} @@ -54,7 +56,7 @@ type CommitterImpl struct { } // NewCommitter new a CommitterImpl with registry configuration -func NewCommitter(registryAddr, registryUsername, registryPassword string) (Committer, error) { +func NewCommitter(registryAddr, registryUsername, registryPassword string, merge bool) (Committer, error) { var conn *grpc.ClientConn var err error @@ -101,16 +103,17 @@ func NewCommitter(registryAddr, registryUsername, registryPassword string) (Comm runtimeServiceClient := runtimeapi.NewRuntimeServiceClient(conn) return &CommitterImpl{ - runtimeServiceClient: runtimeServiceClient, - containerdClient: containerdClient, - conn: conn, - globalOptions: NewGlobalOptionConfig(), - registryAddr: registryAddr, - registryUsername: registryUsername, - registryPassword: registryPassword, - gcContainerMap: make(map[string]struct{}), - gcImageMap: make(map[string]struct{}), - gcInterval: DefaultGcInterval, + runtimeServiceClient: runtimeServiceClient, + containerdClient: containerdClient, + conn: conn, + globalOptions: NewGlobalOptionConfig(), + registryAddr: registryAddr, + registryUsername: registryUsername, + registryPassword: registryPassword, + gcContainerMap: make(map[string]struct{}), + gcImageMap: make(map[string]struct{}), + gcInterval: DefaultGcInterval, + mergeBaseImageTopLayer: merge, }, nil } @@ -130,12 +133,16 @@ func (c *CommitterImpl) CreateContainer(ctx context.Context, devboxName string, // create container with labels originalAnnotations := map[string]string{ v1alpha2.AnnotationContentID: contentID, - v1alpha2.AnnotationInit: AnnotationImageFromValue, v1alpha2.AnnotationStorageLimit: AnnotationUseLimitValue, AnnotationKeyNamespace: DefaultNamespace, AnnotationKeyImageName: baseImage, } + // Add merge base image layers annotation if enabled + if c.mergeBaseImageTopLayer { + originalAnnotations[v1alpha2.AnnotationInit] = AnnotationImageFromValue + } + // convert labels to "containerd.io/snapshot/devbox-" format convertedLabels := convertLabels(originalAnnotations) convertedAnnotations := convertMapToSlice(originalAnnotations) @@ -299,8 +306,9 @@ func (c *CommitterImpl) Commit(ctx context.Context, devboxName string, contentID Stdout: io.Discard, GOptions: *global, Pause: PauseContainerDuringCommit, + // Remove base image top layer: DevboxOptions: types.DevboxOptions{ - RemoveBaseImageTopLayer: DevboxOptionsRemoveBaseImageTopLayer, + RemoveBaseImageTopLayer: c.mergeBaseImageTopLayer, }, } diff --git a/controllers/devbox/internal/commit/commit_test.go b/controllers/devbox/internal/commit/commit_test.go index 7bd3357b67f2..37a851ee23ba 100644 --- a/controllers/devbox/internal/commit/commit_test.go +++ b/controllers/devbox/internal/commit/commit_test.go @@ -23,7 +23,7 @@ const ( // init Committer func TestNewCommitter(t *testing.T) { - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) assert.NotNil(t, committer) } @@ -33,7 +33,7 @@ func TestCommitFlow(t *testing.T) { ctx := context.Background() // 1. create committer - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) // 2. prepare test data @@ -49,7 +49,7 @@ func TestCommitFlow(t *testing.T) { // test create container func TestCreateContainer(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) // create container @@ -68,7 +68,7 @@ func TestCreateContainer(t *testing.T) { // test delete container func TestDeleteContainer(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) // create a container @@ -108,7 +108,7 @@ func TestDeleteContainer(t *testing.T) { // test remove container func TestRemoveContainer(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) // create a container devboxName := fmt.Sprintf("test-devbox-%d", time.Now().Unix()) @@ -147,7 +147,7 @@ func TestRemoveContainer(t *testing.T) { // test error cases func TestErrorCases(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) // test use not exist image to create container @@ -166,7 +166,7 @@ func TestErrorCases(t *testing.T) { // test concurrent operations func TestConcurrentOperations(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) // concurrent to create container @@ -215,7 +215,7 @@ func TestConcurrentOperations(t *testing.T) { // test runtime selection func TestRuntimeSelection(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) // create container with specific runtime @@ -249,7 +249,7 @@ func TestRuntimeSelection(t *testing.T) { // test connection management func TestConnectionManagement(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) defer committer.(*CommitterImpl).Close() @@ -301,7 +301,7 @@ func TestPushToDockerHub(t *testing.T) { registryUser := "cunzili" registryPassword := "123456789" - committer, err := NewCommitter(registryAddr, registryUser, registryPassword) + committer, err := NewCommitter(registryAddr, registryUser, registryPassword, true) if err != nil { t.Errorf("Skip Docker Hub push test: failed to create committer: %v", err) } @@ -362,7 +362,7 @@ func TestPushWithoutAuth(t *testing.T) { registryUser := "" registryPassword := "" - committer, err := NewCommitter(registryAddr, registryUser, registryPassword) + committer, err := NewCommitter(registryAddr, registryUser, registryPassword, true) if err != nil { t.Skipf("Skip no-auth push test: failed to create committer: %v", err) } @@ -393,7 +393,7 @@ func TestPushWithoutAuth(t *testing.T) { // test remove image func TestRemoveImage(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) // create a test devbox name and content id @@ -422,7 +422,7 @@ func TestRemoveImage(t *testing.T) { func TestAtomicLabels(t *testing.T) { ctx := context.Background() ctx = namespaces.WithNamespace(ctx, DefaultNamespace) - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) // 1. create a test container @@ -505,7 +505,7 @@ func TestAtomicLabels(t *testing.T) { // test get image func TestGetImage(t *testing.T) { ctx := context.Background() - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) ctx = namespaces.WithNamespace(ctx, DefaultNamespace) images, err := committer.(*CommitterImpl).containerdClient.ListImages(ctx) @@ -521,7 +521,7 @@ func TestGCFlow(t *testing.T) { ctx = namespaces.WithNamespace(ctx, DefaultNamespace) // create committer - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) defer committer.(*CommitterImpl).Close() @@ -609,7 +609,7 @@ func TestPeriodicGC(t *testing.T) { ctx = namespaces.WithNamespace(ctx, DefaultNamespace) // create committer - committer, err := NewCommitter("", "", "") + committer, err := NewCommitter("", "", "", true) assert.NoError(t, err) err = committer.InitializeGC(ctx) assert.NoError(t, err) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 18e58bd65a64..64f9ad009763 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -64,7 +64,8 @@ type DevboxReconciler struct { PodMatchers []matcher.PodMatcher - DebugMode bool + DebugMode bool + MergeBaseImageTopLayer bool client.Client Scheme *runtime.Scheme @@ -322,12 +323,15 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha2.D return fmt.Errorf("current record is nil") } // create a new pod with default image, with new content id - pod := r.generateDevboxPod(devbox, + podOptions := []helper.DevboxPodOptions{ helper.WithPodImage(currentRecord.BaseImage), helper.WithPodContentID(devbox.Status.ContentID), helper.WithPodNodeName(currentRecord.Node), - helper.WithPodInit(commit.AnnotationImageFromValue), - ) + } + if r.MergeBaseImageTopLayer { + podOptions = append(podOptions, helper.WithPodInit(commit.AnnotationImageFromValue)) + } + pod := r.generateDevboxPod(devbox, podOptions...) if err := r.Create(ctx, pod); err != nil { return err } diff --git a/controllers/go.work.sum b/controllers/go.work.sum index 57c30c282f7a..3309266e117b 100644 --- a/controllers/go.work.sum +++ b/controllers/go.work.sum @@ -2735,6 +2735,7 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= @@ -2906,6 +2907,7 @@ github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzR github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -2995,6 +2997,7 @@ github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= @@ -3343,6 +3346,7 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +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.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -3360,6 +3364,7 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -3371,6 +3376,7 @@ github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec h1:q6XVwXmKvCRHRqesF3cSv6lNqqHi0QWOvgDlSohg8UA= @@ -3387,6 +3393,7 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/studio-b12/gowebdav v0.0.0-20211106090535-29e74efa701f/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg= github.com/sykesm/zap-logfmt v0.0.4 h1:U2WzRvmIWG1wDLCFY3sz8UeEmsdHQjHFNlIdmroVFaI= From 22940b87ea54382cf177dfc23532a1484afdbbc1 Mon Sep 17 00:00:00 2001 From: yy Date: Fri, 10 Oct 2025 17:28:57 +0800 Subject: [PATCH 44/46] add commit time logs --- controllers/devbox/internal/controller/event_handler.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/controllers/devbox/internal/controller/event_handler.go b/controllers/devbox/internal/controller/event_handler.go index 79f7bb806221..dc16f3b65fd1 100644 --- a/controllers/devbox/internal/controller/event_handler.go +++ b/controllers/devbox/internal/controller/event_handler.go @@ -95,11 +95,14 @@ func (h *EventHandler) handleDevboxStateChange(ctx context.Context, event *corev return nil } commitMap.Store(devbox.Status.ContentID, true) + start := time.Now() + h.Logger.Info("start commit devbox", "devbox", devbox.Name, "contentID", devbox.Status.ContentID, "time", start) if err := h.commitDevbox(ctx, devbox, targetState); err != nil { commitMap.Delete(devbox.Status.ContentID) h.Logger.Error(err, "failed to commit devbox", "devbox", devbox.Name) return err } + h.Logger.Info("commit devbox success", "devbox", devbox.Name, "contentID", devbox.Status.ContentID, "time", time.Since(start)) } else if currentState != targetState { // Handle simple state transitions without commit devbox.Status.State = targetState From 5f6d74ddc5c1ebd41a1292b6d6a869b2483d1e00 Mon Sep 17 00:00:00 2001 From: yy Date: Thu, 16 Oct 2025 18:11:07 +0800 Subject: [PATCH 45/46] fix controller ref for secret --- .../internal/controller/devbox_controller.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 64f9ad009763..5e0af4a67225 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -257,24 +257,20 @@ func (r *DevboxReconciler) syncSecret(ctx context.Context, devbox *devboxv1alpha err := r.Get(ctx, client.ObjectKey{Namespace: devbox.Namespace, Name: devbox.Name}, devboxSecret) if err == nil { // Secret already exists, no need to create - + // update controller reference + if err := controllerutil.SetControllerReference(devbox, devboxSecret, r.Scheme); err != nil { + return fmt.Errorf("failed to update owner reference: %w", err) + } // TODO: delete this code after we have a way to sync secret to devbox // check if SEALOS_DEVBOX_JWT_SECRET is exist, if not exist, create it if _, ok := devboxSecret.Data["SEALOS_DEVBOX_JWT_SECRET"]; !ok { devboxSecret.Data["SEALOS_DEVBOX_JWT_SECRET"] = []byte(rand.String(32)) - if err := r.Update(ctx, devboxSecret); err != nil { - return fmt.Errorf("failed to update secret: %w", err) - } } - + // check if SEALOS_DEVBOX_AUTHORIZED_KEYS is exist, if not exist, set it to SEALOS_DEVBOX_PUBLIC_KEY if _, ok := devboxSecret.Data["SEALOS_DEVBOX_AUTHORIZED_KEYS"]; !ok { devboxSecret.Data["SEALOS_DEVBOX_AUTHORIZED_KEYS"] = devboxSecret.Data["SEALOS_DEVBOX_PUBLIC_KEY"] - if err := r.Update(ctx, devboxSecret); err != nil { - return fmt.Errorf("failed to update secret: %w", err) - } } - - return nil + return r.Update(ctx, devboxSecret) } if client.IgnoreNotFound(err) != nil { return fmt.Errorf("failed to get secret: %w", err) From f06675d6ceabe64d6a7dd78ee1df7176e0aeca63 Mon Sep 17 00:00:00 2001 From: yy Date: Mon, 27 Oct 2025 17:05:58 +0800 Subject: [PATCH 46/46] fix devbox release controller --- .../devbox/internal/controller/devboxrelease_controller.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/controllers/devbox/internal/controller/devboxrelease_controller.go b/controllers/devbox/internal/controller/devboxrelease_controller.go index d2bf4f8a51a7..2037b6f6980e 100644 --- a/controllers/devbox/internal/controller/devboxrelease_controller.go +++ b/controllers/devbox/internal/controller/devboxrelease_controller.go @@ -73,6 +73,11 @@ func (r *DevboxreleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reques logger.Error(err, "Failed to get devbox", "devbox", devboxRelease.Spec.DevboxName) return ctrl.Result{}, err } + // if devboxRelease.Status.Phase is success, skip release + if devboxRelease.Status.Phase == devboxv1alpha2.DevBoxReleasePhaseSuccess { + logger.Info("DevBoxRelease is already released, skipping release", "devbox", devboxRelease.Spec.DevboxName, "devboxRelease", devboxRelease.Name, "version", devboxRelease.Spec.Version) + return ctrl.Result{}, nil + } // if devbox is running, skip release if devbox.Status.State == devboxv1alpha2.DevboxStateRunning || devbox.Status.State == devboxv1alpha2.DevboxStatePaused {