diff --git a/.github/workflows/cloud-release.yml b/.github/workflows/cloud-release.yml index a6715e4f9e0..4d8c3454548 100644 --- a/.github/workflows/cloud-release.yml +++ b/.github/workflows/cloud-release.yml @@ -68,6 +68,9 @@ jobs: needs: - release-cloud runs-on: self-hosted + env: + RELEASE_TAG: ${{ github.event.inputs.tag || github.event.release.tag_name }} + OSS_BUCKET: ${{ secrets.OSS_BUCKET }} steps: - name: Checkout uses: actions/checkout@v3 @@ -83,8 +86,8 @@ jobs: sudo sealos version - name: Build run: | - export CLOUD_VERSION=${{ github.event.inputs.tag || github.event.release.tag_name }} - export VERSION=${{ github.event.inputs.tag || github.event.release.tag_name }} + export CLOUD_VERSION="$RELEASE_TAG" + export VERSION="$RELEASE_TAG" export ARCH=amd64 bash ./scripts/cloud/build-offline-tar.sh - name: Setup ossutil @@ -97,13 +100,16 @@ jobs: run: cat ./sealos-cloud.tar.gz.md5 - name: Upload run: | - ossutil cp ./sealos-cloud.tar.gz oss://${{ secrets.OSS_BUCKET }}/cloud/sealos-cloud-${{ github.event.inputs.tag || github.event.release.tag_name }}-amd64.tar.gz - ossutil cp ./sealos-cloud.tar.gz.md5 oss://${{ secrets.OSS_BUCKET }}/cloud/sealos-cloud-${{ github.event.inputs.tag || github.event.release.tag_name }}-amd64.tar.gz.md5 + ossutil cp ./sealos-cloud.tar.gz "oss://$OSS_BUCKET/cloud/sealos-cloud-$RELEASE_TAG-amd64.tar.gz" + ossutil cp ./sealos-cloud.tar.gz.md5 "oss://$OSS_BUCKET/cloud/sealos-cloud-$RELEASE_TAG-amd64.tar.gz.md5" release-arm-offline-tar: needs: - release-cloud runs-on: self-hosted + env: + RELEASE_TAG: ${{ github.event.inputs.tag || github.event.release.tag_name }} + OSS_BUCKET: ${{ secrets.OSS_BUCKET }} steps: - name: Checkout uses: actions/checkout@v3 @@ -119,8 +125,8 @@ jobs: sudo sealos version - name: Build run: | - export CLOUD_VERSION=${{ github.event.inputs.tag || github.event.release.tag_name }} - export VERSION=${{ github.event.inputs.tag || github.event.release.tag_name }} + export CLOUD_VERSION="$RELEASE_TAG" + export VERSION="$RELEASE_TAG" export ARCH=arm64 bash ./scripts/cloud/build-offline-tar.sh - name: Setup ossutil @@ -133,5 +139,5 @@ jobs: run: cat ./sealos-cloud.tar.gz.md5 - name: Upload run: | - ossutil cp ./sealos-cloud.tar.gz oss://${{ secrets.OSS_BUCKET }}/cloud/sealos-cloud-${{ github.event.inputs.tag || github.event.release.tag_name }}-arm64.tar.gz - ossutil cp ./sealos-cloud.tar.gz.md5 oss://${{ secrets.OSS_BUCKET }}/cloud/sealos-cloud-${{ github.event.inputs.tag || github.event.release.tag_name }}-arm64.tar.gz.md5 + ossutil cp ./sealos-cloud.tar.gz "oss://$OSS_BUCKET/cloud/sealos-cloud-$RELEASE_TAG-arm64.tar.gz" + ossutil cp ./sealos-cloud.tar.gz.md5 "oss://$OSS_BUCKET/cloud/sealos-cloud-$RELEASE_TAG-arm64.tar.gz.md5" diff --git a/cmd/sealos/cmd/reset.go b/cmd/sealos/cmd/reset.go index c08506878ef..e98c6a83148 100644 --- a/cmd/sealos/cmd/reset.go +++ b/cmd/sealos/cmd/reset.go @@ -31,8 +31,8 @@ reset you current cluster: func newResetCmd() *cobra.Command { resetArgs := &apply.ResetArgs{ - Cluster: &apply.Cluster{}, - SSH: &apply.SSH{}, + ClusterName: &apply.ClusterName{}, + SSH: &apply.SSH{}, } var resetCmd = &cobra.Command{ diff --git a/controllers/account/config/rbac/role.yaml b/controllers/account/config/rbac/role.yaml index 723a2837e5d..ae3b9773c50 100644 --- a/controllers/account/config/rbac/role.yaml +++ b/controllers/account/config/rbac/role.yaml @@ -337,6 +337,7 @@ rules: resources: - users verbs: + - create - get - list - watch diff --git a/controllers/account/controllers/account_controller.go b/controllers/account/controllers/account_controller.go index ff5d6c012d7..7024d150cfb 100644 --- a/controllers/account/controllers/account_controller.go +++ b/controllers/account/controllers/account_controller.go @@ -86,7 +86,7 @@ type AccountReconciler struct { //+kubebuilder:rbac:groups=account.sealos.io,resources=accounts/finalizers,verbs=update //+kubebuilder:rbac:groups=core,resources=resourcequotas,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=core,resources=limitranges,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=user.sealos.io,resources=users,verbs=get;list;watch +//+kubebuilder:rbac:groups=user.sealos.io,resources=users,verbs=create;get;list;watch //+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete diff --git a/controllers/account/deploy/manifests/deploy.yaml b/controllers/account/deploy/manifests/deploy.yaml index 0dbf09c8853..6ae9808efe5 100644 --- a/controllers/account/deploy/manifests/deploy.yaml +++ b/controllers/account/deploy/manifests/deploy.yaml @@ -550,6 +550,7 @@ rules: resources: - users verbs: + - create - get - list - watch diff --git a/controllers/devbox/api/v1alpha1/runtime_types.go b/controllers/devbox/api/v1alpha1/runtime_types.go index cf1e6bf8045..52791dbf79b 100644 --- a/controllers/devbox/api/v1alpha1/runtime_types.go +++ b/controllers/devbox/api/v1alpha1/runtime_types.go @@ -72,12 +72,19 @@ type Component struct { Version string `json:"version"` } +type RuntimeState string + +const ( + RuntimeStateActive RuntimeState = "active" + RuntimeStateDeprecated RuntimeState = "deprecated" +) + // RuntimeSpec defines the desired state of Runtime type RuntimeSpec struct { - // +kubebuilder:validation:Required - Version string `json:"version"` // +kubebuilder:validation:Required ClassRef string `json:"classRef"` + // +kubebuilder:validation:Required + Version string `json:"version"` // +kubebuilder:validation:Optional Components []Component `json:"components,omitempty"` @@ -88,6 +95,13 @@ type RuntimeSpec struct { // +kubebuilder:validation:Required Config Config `json:"config"` + + // +kubebuilder:validation:Optional + RuntimeVersion string `json:"runtimeVersion,omitempty"` + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Enum=active;deprecated + // +kubebuilder:default=active + State RuntimeState `json:"state,omitempty"` } // RuntimeStatus defines the observed state of Runtime @@ -98,6 +112,10 @@ type RuntimeStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Class",type=string,JSONPath=`.spec.classRef` +// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version` +// +kubebuilder:printcolumn:name="RuntimeVersion",type=string,JSONPath=`.spec.runtimeVersion` +// +kubebuilder:printcolumn:name="State",type=string,JSONPath=`.spec.state` // Runtime is the Schema for the runtimes API type Runtime struct { diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 75e046aa587..fba7e1dd261 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -69,7 +69,8 @@ func main() { var registryUser string var registryPassword string var authAddr string - var ephemeralStorage string + var requestEphemeralStorage string + var limitEphemeralStorage string var debugMode bool flag.StringVar(®istryAddr, "registry-addr", "sealos.hub:5000", "The address of the registry") flag.StringVar(®istryUser, "registry-user", "admin", "The user of the registry") @@ -86,7 +87,8 @@ func main() { flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") flag.BoolVar(&debugMode, "debug", false, "If set, debug mode will be enabled") - flag.StringVar(&ephemeralStorage, "ephemeral-storage", "2000Mi", "The maximum value of equatorial storage in devbox.") + flag.StringVar(&requestEphemeralStorage, "request-ephemeral-storage", "500Mi", "The request value of ephemeral storage in devbox.") + flag.StringVar(&limitEphemeralStorage, "limit-ephemeral-storage", "10Gi", "The limit value of ephemeral storage in devbox.") opts := zap.Options{ Development: true, } @@ -177,12 +179,13 @@ func main() { } if err = (&controller.DevboxReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - CommitImageRegistry: registryAddr, - Recorder: mgr.GetEventRecorderFor("devbox-controller"), - EquatorialStorage: ephemeralStorage, - DebugMode: debugMode, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + CommitImageRegistry: registryAddr, + Recorder: mgr.GetEventRecorderFor("devbox-controller"), + RequestEphemeralStorage: requestEphemeralStorage, + LimitEphemeralStorage: limitEphemeralStorage, + DebugMode: debugMode, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Devbox") os.Exit(1) diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimes.yaml index bfaaa6eccbf..00ed31895c3 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimes.yaml @@ -28,7 +28,20 @@ spec: singular: runtime scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .spec.classRef + name: Class + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.runtimeVersion + name: RuntimeVersion + type: string + - jsonPath: .spec.state + name: State + type: string + name: v1alpha1 schema: openAPIV3Schema: description: Runtime is the Schema for the runtimes API @@ -2002,6 +2015,14 @@ spec: type: object description: type: string + runtimeVersion: + type: string + state: + default: active + enum: + - active + - deprecated + type: string version: type: string required: diff --git a/controllers/devbox/deploy/manifests/deploy.yaml.tmpl b/controllers/devbox/deploy/manifests/deploy.yaml.tmpl index 92d3b7c6a9d..7286e29b5b8 100644 --- a/controllers/devbox/deploy/manifests/deploy.yaml.tmpl +++ b/controllers/devbox/deploy/manifests/deploy.yaml.tmpl @@ -3129,7 +3129,20 @@ spec: singular: runtime scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .spec.classRef + name: Class + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.runtimeVersion + name: RuntimeVersion + type: string + - jsonPath: .spec.state + name: State + type: string + name: v1alpha1 schema: openAPIV3Schema: description: Runtime is the Schema for the runtimes API @@ -5103,6 +5116,14 @@ spec: type: object description: type: string + runtimeVersion: + type: string + state: + default: active + enum: + - active + - deprecated + type: string version: type: string required: diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index a74fef4893e..5fb887bd0fc 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -44,8 +44,9 @@ import ( // DevboxReconciler reconciles a Devbox object type DevboxReconciler struct { - CommitImageRegistry string - EquatorialStorage string + CommitImageRegistry string + RequestEphemeralStorage string + LimitEphemeralStorage string DebugMode bool @@ -169,6 +170,15 @@ 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 + + // 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) + } + } return nil } if client.IgnoreNotFound(err) != nil { @@ -185,6 +195,7 @@ func (r *DevboxReconciler) syncSecret(ctx context.Context, devbox *devboxv1alpha ObjectMeta: objectMeta, Data: map[string][]byte{ "SEALOS_DEVBOX_PASSWORD": []byte(rand.String(12)), + "SEALOS_DEVBOX_JWT_SECRET": []byte(rand.String(32)), "SEALOS_DEVBOX_PUBLIC_KEY": publicKey, "SEALOS_DEVBOX_PRIVATE_KEY": privateKey, }, @@ -526,7 +537,7 @@ func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, runt WorkingDir: helper.GenerateWorkingDir(devbox, runtime), Command: helper.GenerateCommand(devbox, runtime), Args: helper.GenerateDevboxArgs(devbox, runtime), - Resources: helper.GenerateResourceRequirements(devbox, r.EquatorialStorage), + Resources: helper.GenerateResourceRequirements(devbox, r.RequestEphemeralStorage, r.LimitEphemeralStorage), }, } diff --git a/controllers/devbox/internal/controller/helper/devbox.go b/controllers/devbox/internal/controller/helper/devbox.go index 7ce9ac497f5..a84032eb48a 100644 --- a/controllers/devbox/internal/controller/helper/devbox.go +++ b/controllers/devbox/internal/controller/helper/devbox.go @@ -218,6 +218,16 @@ func PodMatchExpectations(expectPod *corev1.Pod, pod *corev1.Pod) bool { return false } + // Check Ephemeral Storage changes + if container.Resources.Requests.StorageEphemeral().Cmp(*expectContainer.Resources.Requests.StorageEphemeral()) != 0 { + slog.Info("Ephemeral-Storage requests are not equal") + return false + } + if container.Resources.Limits.StorageEphemeral().Cmp(*expectContainer.Resources.Limits.StorageEphemeral()) != 0 { + slog.Info("Ephemeral-Storage limits are not equal") + return false + } + // Check environment variables if len(container.Env) != len(expectContainer.Env) { return false @@ -370,18 +380,19 @@ func GenerateSSHVolume(devbox *devboxv1alpha1.Devbox) corev1.Volume { } } -func GenerateResourceRequirements(devbox *devboxv1alpha1.Devbox, equatorialStorage string) corev1.ResourceRequirements { +func GenerateResourceRequirements(devbox *devboxv1alpha1.Devbox, requestEphemeralStorage, limitEphemeralStorage string) corev1.ResourceRequirements { return corev1.ResourceRequirements{ Requests: calculateResourceRequest( corev1.ResourceList{ - corev1.ResourceCPU: devbox.Spec.Resource["cpu"], - corev1.ResourceMemory: devbox.Spec.Resource["memory"], + corev1.ResourceCPU: devbox.Spec.Resource["cpu"], + corev1.ResourceMemory: devbox.Spec.Resource["memory"], + corev1.ResourceEphemeralStorage: resource.MustParse(requestEphemeralStorage), }, ), Limits: corev1.ResourceList{ - "cpu": devbox.Spec.Resource["cpu"], - "memory": devbox.Spec.Resource["memory"], - "ephemeral-storage": resource.MustParse(equatorialStorage), + corev1.ResourceCPU: devbox.Spec.Resource["cpu"], + corev1.ResourceMemory: devbox.Spec.Resource["memory"], + corev1.ResourceEphemeralStorage: resource.MustParse(limitEphemeralStorage), }, } } @@ -403,6 +414,11 @@ func calculateResourceRequest(limit corev1.ResourceList) corev1.ResourceList { memoryRequest := memoryValue / rate request[corev1.ResourceMemory] = *resource.NewQuantity(int64(memoryRequest), resource.BinarySI) } + + if ephemeralStorage, ok := limit[corev1.ResourceEphemeralStorage]; ok { + request[corev1.ResourceEphemeralStorage] = ephemeralStorage + } + return request } diff --git a/controllers/objectstorage/api/v1/objectstorageuser_types.go b/controllers/objectstorage/api/v1/objectstorageuser_types.go index 4728948d9fd..98d582a71ea 100644 --- a/controllers/objectstorage/api/v1/objectstorageuser_types.go +++ b/controllers/objectstorage/api/v1/objectstorageuser_types.go @@ -22,6 +22,8 @@ import ( // ObjectStorageUserSpec defines the desired state of ObjectStorageUser type ObjectStorageUserSpec struct { + // +kubebuilder:default=0 + SecretKeyVersion int64 `json:"secretKeyVersion,omitempty"` } // ObjectStorageUserStatus defines the observed state of ObjectStorageUser @@ -34,6 +36,8 @@ type ObjectStorageUserStatus struct { SecretKey string `json:"secretKey,omitempty"` Internal string `json:"internal,omitempty"` External string `json:"external,omitempty"` + // +kubebuilder:default=0 + SecretKeyVersion int64 `json:"secretKeyVersion,omitempty"` } //+kubebuilder:object:root=true diff --git a/controllers/objectstorage/config/crd/bases/objectstorage.sealos.io_objectstorageusers.yaml b/controllers/objectstorage/config/crd/bases/objectstorage.sealos.io_objectstorageusers.yaml index d4f1fa1e20e..8766ee1fb2c 100644 --- a/controllers/objectstorage/config/crd/bases/objectstorage.sealos.io_objectstorageusers.yaml +++ b/controllers/objectstorage/config/crd/bases/objectstorage.sealos.io_objectstorageusers.yaml @@ -48,6 +48,11 @@ spec: type: object spec: description: ObjectStorageUserSpec defines the desired state of ObjectStorageUser + properties: + secretKeyVersion: + default: 0 + format: int64 + type: integer type: object status: description: ObjectStorageUserStatus defines the observed state of ObjectStorageUser @@ -66,6 +71,10 @@ spec: type: integer secretKey: type: string + secretKeyVersion: + default: 0 + format: int64 + type: integer size: description: unit is byte format: int64 diff --git a/controllers/objectstorage/controllers/objectstorageuser_controller.go b/controllers/objectstorage/controllers/objectstorageuser_controller.go index 722d912da05..0b85da99b4d 100644 --- a/controllers/objectstorage/controllers/objectstorageuser_controller.go +++ b/controllers/objectstorage/controllers/objectstorageuser_controller.go @@ -153,6 +153,15 @@ func (r *ObjectStorageUserReconciler) Reconcile(ctx context.Context, req ctrl.Re updated := r.initObjectStorageUser(user, username, quota.Value()) + pwdUpdated := false + + if user.Spec.SecretKeyVersion > user.Status.SecretKeyVersion { + user.Status.SecretKey = rand.String(16) + user.Status.SecretKeyVersion = user.Spec.SecretKeyVersion + pwdUpdated = true + updated = true + } + accessKey := user.Status.AccessKey secretKey := user.Status.SecretKey @@ -170,6 +179,13 @@ func (r *ObjectStorageUserReconciler) Reconcile(ctx context.Context, req ctrl.Re } } + if pwdUpdated { + if err := r.OSAdminClient.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled); err != nil { + r.Logger.Error(err, "failed to set user secret key", "name", accessKey) + } + r.Logger.V(1).Info("[user] password change info", "name", user.Name, "spec secret key version", user.Spec.SecretKeyVersion) + } + secret := &corev1.Secret{} if err := r.Get(ctx, client.ObjectKey{Name: OSKeySecret, Namespace: userNamespace}, secret); err != nil { if !errors.IsNotFound(err) { diff --git a/controllers/terminal/api/v1/terminal_types.go b/controllers/terminal/api/v1/terminal_types.go index 49701dd8bcf..97b6dca1006 100644 --- a/controllers/terminal/api/v1/terminal_types.go +++ b/controllers/terminal/api/v1/terminal_types.go @@ -56,6 +56,7 @@ type TerminalSpec struct { type TerminalStatus struct { AvailableReplicas int32 `json:"availableReplicas"` ServiceName string `json:"serviceName"` + SecretHeader string `json:"secretHeader"` Domain string `json:"domain"` } @@ -64,7 +65,6 @@ type TerminalStatus struct { //+kubebuilder:printcolumn:name="User",type=string,JSONPath=".spec.user" //+kubebuilder:printcolumn:name="Keepalived",type=string,JSONPath=".spec.keepalived" //+kubebuilder:printcolumn:name="Domain",type=string,JSONPath=".status.domain" -//+kubebuilder:printcolumn:name="APIServer",priority=1,type=string,JSONPath=".spec.apiServer" //+kubebuilder:printcolumn:name="LastUpdateTime",priority=1,type=string,JSONPath=".metadata.annotations.lastUpdateTime" //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" diff --git a/controllers/terminal/config/crd/bases/terminal.sealos.io_terminals.yaml b/controllers/terminal/config/crd/bases/terminal.sealos.io_terminals.yaml index bb422284113..4ef9bcee596 100644 --- a/controllers/terminal/config/crd/bases/terminal.sealos.io_terminals.yaml +++ b/controllers/terminal/config/crd/bases/terminal.sealos.io_terminals.yaml @@ -38,10 +38,6 @@ spec: - jsonPath: .status.domain name: Domain type: string - - jsonPath: .spec.apiServer - name: APIServer - priority: 1 - type: string - jsonPath: .metadata.annotations.lastUpdateTime name: LastUpdateTime priority: 1 @@ -107,11 +103,14 @@ spec: type: integer domain: type: string + secretHeader: + type: string serviceName: type: string required: - availableReplicas - domain + - secretHeader - serviceName type: object type: object diff --git a/controllers/terminal/controllers/ingress.go b/controllers/terminal/controllers/ingress.go index 5f51091e5be..4ddaa93d563 100644 --- a/controllers/terminal/controllers/ingress.go +++ b/controllers/terminal/controllers/ingress.go @@ -37,6 +37,15 @@ if ($flag = '02'){ return 403; }` func (r *TerminalReconciler) createNginxIngress(terminal *terminalv1.Terminal, host string) *networkingv1.Ingress { cors := fmt.Sprintf("https://%s,https://*.%s", r.CtrConfig.Global.CloudDomain+r.getPort(), r.CtrConfig.Global.CloudDomain+r.getPort()) + secretHeader := terminal.Status.SecretHeader + configurationSnippet := safeConfigurationSnippet + ` +proxy_set_header Authorization ""; +proxy_set_header ` + secretHeader + ` "1";` + + higressReqHeaderUpdate := ` +Authorization "" +` + secretHeader + ` "1"` + objectMeta := metav1.ObjectMeta{ Name: terminal.Name, Namespace: terminal.Namespace, @@ -50,7 +59,8 @@ func (r *TerminalReconciler) createNginxIngress(terminal *terminalv1.Terminal, h "nginx.ingress.kubernetes.io/cors-allow-origin": cors, "nginx.ingress.kubernetes.io/cors-allow-methods": "PUT, GET, POST, PATCH, OPTIONS", "nginx.ingress.kubernetes.io/cors-allow-credentials": "false", - "nginx.ingress.kubernetes.io/configuration-snippet": safeConfigurationSnippet, + "nginx.ingress.kubernetes.io/configuration-snippet": configurationSnippet, + "higress.io/request-header-control-update": higressReqHeaderUpdate, }, } diff --git a/controllers/terminal/controllers/terminal_controller.go b/controllers/terminal/controllers/terminal_controller.go index 355d034924a..8035da62a65 100644 --- a/controllers/terminal/controllers/terminal_controller.go +++ b/controllers/terminal/controllers/terminal_controller.go @@ -18,6 +18,7 @@ package controllers import ( "context" + "strings" "time" "github.com/jaevor/go-nanoid" @@ -32,9 +33,11 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" "github.com/labring/sealos/controllers/pkg/utils/label" terminalv1 "github.com/labring/sealos/controllers/terminal/api/v1" @@ -65,6 +68,10 @@ const ( MemoryLimit = "256Mi" ) +const ( + SecretHeaderPrefix = "X-SEALOS-" +) + // TerminalReconciler reconciles a Terminal object type TerminalReconciler struct { client.Client @@ -123,6 +130,13 @@ func (r *TerminalReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } } + if terminal.Status.SecretHeader == "" { + terminal.Status.SecretHeader = r.generateSecretHeader() + if err := r.Status().Update(ctx, terminal); err != nil { + return ctrl.Result{}, err + } + } + recLabels := label.RecommendedLabels(&label.Recommended{ Name: terminal.Name, ManagedBy: label.DefaultManagedBy, @@ -262,6 +276,8 @@ func (r *TerminalReconciler) syncDeployment(ctx context.Context, terminal *termi {Name: "USER_TOKEN", Value: terminal.Spec.Token}, {Name: "NAMESPACE", Value: terminal.Namespace}, {Name: "USER_NAME", Value: terminal.Spec.User}, + // Add secret header + {Name: "AUTH_HEADER", Value: terminal.Status.SecretHeader}, } containers = []corev1.Container{ @@ -377,12 +393,18 @@ func (r *TerminalReconciler) getPort() string { return ":" + r.CtrConfig.Global.CloudPort } +func (r *TerminalReconciler) generateSecretHeader() string { + return SecretHeaderPrefix + strings.ToUpper(rand.String(5)) +} + // SetupWithManager sets up the controller with the Manager. func (r *TerminalReconciler) SetupWithManager(mgr ctrl.Manager) error { r.recorder = mgr.GetEventRecorderFor("sealos-terminal-controller") r.Config = mgr.GetConfig() return ctrl.NewControllerManagedBy(mgr). - For(&terminalv1.Terminal{}). - Owns(&appsv1.Deployment{}).Owns(&corev1.Service{}).Owns(&networkingv1.Ingress{}). + For(&terminalv1.Terminal{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). + Owns(&appsv1.Deployment{}, builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})). + Owns(&corev1.Service{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). + Owns(&networkingv1.Ingress{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). Complete(r) } diff --git a/controllers/terminal/deploy/manifests/deploy.yaml.tmpl b/controllers/terminal/deploy/manifests/deploy.yaml.tmpl index 51e6eb50941..c92a9e8c6c8 100644 --- a/controllers/terminal/deploy/manifests/deploy.yaml.tmpl +++ b/controllers/terminal/deploy/manifests/deploy.yaml.tmpl @@ -31,10 +31,6 @@ spec: - jsonPath: .status.domain name: Domain type: string - - jsonPath: .spec.apiServer - name: APIServer - priority: 1 - type: string - jsonPath: .metadata.annotations.lastUpdateTime name: LastUpdateTime priority: 1 @@ -91,11 +87,14 @@ spec: type: integer domain: type: string + secretHeader: + type: string serviceName: type: string required: - availableReplicas - domain + - secretHeader - serviceName type: object type: object diff --git a/controllers/user/Makefile b/controllers/user/Makefile index 3fada975794..b585fcd4c90 100644 --- a/controllers/user/Makefile +++ b/controllers/user/Makefile @@ -123,8 +123,8 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions -KUSTOMIZE_VERSION ?= v3.8.7 -CONTROLLER_TOOLS_VERSION ?= v0.10.0 +KUSTOMIZE_VERSION ?= v5.3.0 +CONTROLLER_TOOLS_VERSION ?= v0.14.0 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize diff --git a/controllers/user/api/v1/deleterequest_types.go b/controllers/user/api/v1/deleterequest_types.go index 387badd52a2..7b4ca3dd1fb 100644 --- a/controllers/user/api/v1/deleterequest_types.go +++ b/controllers/user/api/v1/deleterequest_types.go @@ -36,6 +36,7 @@ type DeleteRequestStatus struct { // +kubebuilder:resource:scope=Cluster //+kubebuilder:printcolumn:name="User",type="string",JSONPath=".spec.user" //+kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" +//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" // DeleteRequest is the Schema for the deleterequests API type DeleteRequest struct { diff --git a/controllers/user/api/v1/operationrequest_types.go b/controllers/user/api/v1/operationrequest_types.go index 481345252e1..8b0d2adf294 100644 --- a/controllers/user/api/v1/operationrequest_types.go +++ b/controllers/user/api/v1/operationrequest_types.go @@ -22,7 +22,9 @@ import ( // OperationrequestSpec defines the desired state of Operationrequest type OperationrequestSpec struct { - User string `json:"user,omitempty"` + // Namespace is the workspace that needs to be operated. + Namespace string `json:"namespace,omitempty"` + User string `json:"user,omitempty"` // +kubebuilder:validation:Enum=Owner;Manager;Developer Role RoleType `json:"role,omitempty"` // +kubebuilder:validation:Enum=Grant;Update;Deprive @@ -56,9 +58,11 @@ const ( ) //+kubebuilder:printcolumn:name="Action",type="string",JSONPath=".spec.action" +//+kubebuilder:printcolumn:name="Namespace",type="string",JSONPath=".spec.namespace" //+kubebuilder:printcolumn:name="User",type="string",JSONPath=".spec.user" //+kubebuilder:printcolumn:name="Role",type="string",JSONPath=".spec.role" //+kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" +//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" //+kubebuilder:object:root=true //+kubebuilder:subresource:status diff --git a/controllers/user/config/crd/bases/user.sealos.io_deleterequests.yaml b/controllers/user/config/crd/bases/user.sealos.io_deleterequests.yaml index a457df17460..575311e7219 100644 --- a/controllers/user/config/crd/bases/user.sealos.io_deleterequests.yaml +++ b/controllers/user/config/crd/bases/user.sealos.io_deleterequests.yaml @@ -17,8 +17,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: deleterequests.user.sealos.io spec: group: user.sealos.io @@ -36,20 +35,28 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: description: DeleteRequest is the Schema for the deleterequests API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object diff --git a/controllers/user/config/crd/bases/user.sealos.io_operationrequests.yaml b/controllers/user/config/crd/bases/user.sealos.io_operationrequests.yaml index 96537708dec..cfabdc44c85 100644 --- a/controllers/user/config/crd/bases/user.sealos.io_operationrequests.yaml +++ b/controllers/user/config/crd/bases/user.sealos.io_operationrequests.yaml @@ -17,8 +17,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: operationrequests.user.sealos.io spec: group: user.sealos.io @@ -33,6 +32,9 @@ spec: - jsonPath: .spec.action name: Action type: string + - jsonPath: .spec.namespace + name: Namespace + type: string - jsonPath: .spec.user name: User type: string @@ -42,20 +44,28 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: description: Operationrequest is the Schema for the operation requests API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -68,6 +78,9 @@ spec: - Update - Deprive type: string + namespace: + description: Namespace is the workspace that needs to be operated. + type: string role: enum: - Owner diff --git a/controllers/user/config/crd/bases/user.sealos.io_users.yaml b/controllers/user/config/crd/bases/user.sealos.io_users.yaml index 74a0a38c674..b11d6470840 100644 --- a/controllers/user/config/crd/bases/user.sealos.io_users.yaml +++ b/controllers/user/config/crd/bases/user.sealos.io_users.yaml @@ -17,8 +17,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: users.user.sealos.io spec: group: user.sealos.io @@ -45,14 +44,19 @@ spec: description: User is the Schema for the users API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -61,12 +65,14 @@ spec: properties: csrExpirationSeconds: default: 7200 - description: "expirationSeconds is the requested duration of validity - of the issued certificate. The certificate signer may issue a certificate - with a different validity duration so a client must check the delta - between the notBefore and notAfter fields in the issued certificate - to determine the actual duration. \n The minimum valid value for - expirationSeconds is 600, i.e. 10 minutes." + description: |- + expirationSeconds is the requested duration of validity of the issued + certificate. The certificate signer may issue a certificate with a different + validity duration so a client must check the delta between the notBefore and + notAfter fields in the issued certificate to determine the actual duration. + + + The minimum valid value for expirationSeconds is 600, i.e. 10 minutes. format: int32 type: integer type: object diff --git a/controllers/user/config/rbac/role.yaml b/controllers/user/config/rbac/role.yaml index a08d957d42f..4ce169d6ed5 100644 --- a/controllers/user/config/rbac/role.yaml +++ b/controllers/user/config/rbac/role.yaml @@ -16,7 +16,6 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: diff --git a/controllers/user/config/webhook/manifests.yaml b/controllers/user/config/webhook/manifests.yaml index 7048ff3da51..f79982aeb70 100644 --- a/controllers/user/config/webhook/manifests.yaml +++ b/controllers/user/config/webhook/manifests.yaml @@ -16,7 +16,6 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -63,7 +62,6 @@ webhooks: apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: diff --git a/controllers/user/controllers/operationrequest_controller.go b/controllers/user/controllers/operationrequest_controller.go index d9bcab550d8..1691fd51436 100644 --- a/controllers/user/controllers/operationrequest_controller.go +++ b/controllers/user/controllers/operationrequest_controller.go @@ -19,8 +19,13 @@ package controllers import ( "context" "fmt" + "sync" "time" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "github.com/go-logr/logr" util "github.com/labring/operator-sdk/controller" @@ -48,6 +53,7 @@ type OperationReqReconciler struct { Logger logr.Logger Scheme *runtime.Scheme Recorder record.EventRecorder + userLock map[string]*sync.Mutex // expirationTime is the time duration of the request is expired expirationTime time.Duration @@ -68,9 +74,10 @@ func (r *OperationReqReconciler) SetupWithManager(mgr ctrl.Manager, opts util.Ra r.Scheme = mgr.GetScheme() r.expirationTime = expTime r.retentionTime = retTime + r.userLock = make(map[string]*sync.Mutex) r.Logger.V(1).Info("init reconcile operationrequest controller") return ctrl.NewControllerManagedBy(mgr). - For(&userv1.Operationrequest{}). + For(&userv1.Operationrequest{}, builder.WithPredicates(namespaceOnlyPredicate(config.GetUserSystemNamespace()))). WithOptions(controller.Options{ MaxConcurrentReconciles: util.GetConcurrent(opts), RateLimiter: util.GetRateLimiter(opts), @@ -78,6 +85,23 @@ func (r *OperationReqReconciler) SetupWithManager(mgr ctrl.Manager, opts util.Ra Complete(r) } +func namespaceOnlyPredicate(namespace string) predicate.Predicate { + return predicate.Funcs{ + CreateFunc: func(e event.CreateEvent) bool { + return e.Object.GetNamespace() == namespace + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return e.Object.GetNamespace() == namespace + }, + UpdateFunc: func(e event.UpdateEvent) bool { + return e.ObjectNew.GetNamespace() == namespace + }, + GenericFunc: func(e event.GenericEvent) bool { + return e.Object.GetNamespace() == namespace + }, + } +} + // +kubebuilder:rbac:groups=user.sealos.io,resources=operationrequests,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=user.sealos.io,resources=operationrequests/status,verbs=get;update;patch // +kubebuilder:rbac:groups=user.sealos.io,resources=operationrequests/finalizers,verbs=update @@ -91,6 +115,13 @@ func (r *OperationReqReconciler) Reconcile(ctx context.Context, req ctrl.Request } func (r *OperationReqReconciler) reconcile(ctx context.Context, request *userv1.Operationrequest) (ctrl.Result, error) { + userLock, ok := r.userLock[request.Spec.User] + if !ok { + userLock = &sync.Mutex{} + r.userLock[request.Spec.User] = userLock + } + userLock.Lock() + defer userLock.Unlock() r.Logger.V(1).Info("start reconcile controller operationRequest", getLog(request)...) // count the time cost of handling the request startTime := time.Now() @@ -136,17 +167,10 @@ func (r *OperationReqReconciler) reconcile(ctx context.Context, request *userv1. ) user := &userv1.User{} - if err := r.Get(ctx, client.ObjectKey{Name: config.GetUserNameByNamespace(request.Namespace)}, user); err != nil { + if err := r.Get(ctx, client.ObjectKey{Name: config.GetUserNameByNamespace(request.Spec.Namespace)}, user); err != nil { r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to get user", "Failed to get user %s", request.Spec.User) return ctrl.Result{}, err } - if request.Spec.Role == userv1.OwnerRoleType { - if user.Name == user.Annotations[userv1.UserAnnotationOwnerKey] { - // 不允许转移个人空间 - r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to grant role", "Failed to grant role %s to user %s, cannot transfer personal workspace", request.Spec.Role, request.Spec.User) - return ctrl.Result{}, r.updateRequestStatus(ctx, request, userv1.RequestFailed) - } - } bindUser := &userv1.User{} if err := r.Get(ctx, client.ObjectKey{Name: request.Spec.User}, bindUser); err != nil { r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to get bind user", "Failed to get bind user %s", request.Spec.User) @@ -184,8 +208,12 @@ func (r *OperationReqReconciler) reconcile(ctx context.Context, request *userv1. r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to delete rolebinding", "Failed to delete rolebinding %s/%s", rolebinding.Namespace, rolebinding.Name) return ctrl.Result{}, err } - if _, err := ctrl.CreateOrUpdate(ctx, r.Client, rolebinding, setUpOwnerReferenceFc); err != nil { - r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to create/update rolebinding", "Failed to create rolebinding %s/%s", rolebinding.Namespace, rolebinding.Name) + if err := r.Create(ctx, rolebinding); err != nil { + r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to create rolebinding", "Failed to create rolebinding %s/%s", rolebinding.Namespace, rolebinding.Name) + return ctrl.Result{}, err + } + if err = setUpOwnerReferenceFc(); err != nil { + r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to set owner reference", "Failed to set owner reference for rolebinding %s/%s", rolebinding.Namespace, rolebinding.Name) return ctrl.Result{}, err } if request.Spec.Role == userv1.OwnerRoleType { @@ -206,7 +234,7 @@ func (r *OperationReqReconciler) reconcile(ctx context.Context, request *userv1. return ctrl.Result{}, err } - r.Recorder.Eventf(request, v1.EventTypeNormal, "Completed", "Completed operation request %s/%s", request.Namespace, request.Name) + r.Recorder.Eventf(request, v1.EventTypeNormal, "Completed", "Completed operation request %s/%s", request.Spec.Namespace, request.Name) return ctrl.Result{RequeueAfter: OperationReqRequeueDuration}, nil } @@ -234,9 +262,9 @@ func (r *OperationReqReconciler) isExpired(request *userv1.Operationrequest) boo func (r *OperationReqReconciler) deleteRequest(ctx context.Context, request *userv1.Operationrequest) error { r.Logger.V(1).Info("deleting OperationRequest", "request", request) if err := r.Delete(ctx, request); client.IgnoreNotFound(err) != nil { - r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to delete OperationRequest", "Failed to delete OperationRequest %s/%s", request.Namespace, request.Name) + r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to delete OperationRequest", "Failed to delete OperationRequest %s/%s", request.Spec.Namespace, request.Name) r.Logger.Error(err, "Failed to delete OperationRequest", getLog(request)...) - return fmt.Errorf("failed to delete OperationRequest %s/%s: %w", request.Namespace, request.Name, err) + return fmt.Errorf("failed to delete OperationRequest %s/%s: %w", request.Spec.Namespace, request.Name, err) } r.Logger.V(1).Info("delete OperationRequest success", getLog(request)...) return nil @@ -245,7 +273,7 @@ func (r *OperationReqReconciler) deleteRequest(ctx context.Context, request *use func (r *OperationReqReconciler) updateRequestStatus(ctx context.Context, request *userv1.Operationrequest, phase userv1.RequestPhase) error { request.Status.Phase = phase if err := r.Status().Update(ctx, request); err != nil { - r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to update OperationRequest status", "Failed to update OperationRequest status %s/%s", request.Namespace, request.Name) + r.Recorder.Eventf(request, v1.EventTypeWarning, "Failed to update OperationRequest status", "Failed to update OperationRequest status %s/%s", request.Spec.Namespace, request.Name) r.Logger.V(1).Info("update OperationRequest status failed", getLog(request)...) return err } @@ -257,7 +285,7 @@ func conventRequestToRolebinding(request *userv1.Operationrequest) *rbacv1.RoleB return &rbacv1.RoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: config.GetGroupRoleBindingName(request.Spec.User), - Namespace: request.Namespace, + Namespace: request.Spec.Namespace, Annotations: map[string]string{ userAnnotationOwnerKey: request.Spec.User, }, @@ -283,7 +311,7 @@ func conventRequestToRolebinding(request *userv1.Operationrequest) *rbacv1.RoleB func getLog(request *userv1.Operationrequest, kv ...interface{}) []interface{} { return append([]interface{}{ "request.name", request.Name, - "request.namespace", request.Namespace, + "request.Spec.Namespace", request.Spec.Namespace, "request.user", request.Spec.User, "request.role", request.Spec.Role, "request.action", request.Spec.Action, diff --git a/controllers/user/deploy/manifests/deploy.yaml.tmpl b/controllers/user/deploy/manifests/deploy.yaml.tmpl index 7ab425ebe02..c9a83881014 100644 --- a/controllers/user/deploy/manifests/deploy.yaml.tmpl +++ b/controllers/user/deploy/manifests/deploy.yaml.tmpl @@ -9,8 +9,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: deleterequests.user.sealos.io spec: group: user.sealos.io @@ -28,20 +27,28 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: description: DeleteRequest is the Schema for the deleterequests API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -72,8 +79,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: operationrequests.user.sealos.io spec: group: user.sealos.io @@ -88,6 +94,9 @@ spec: - jsonPath: .spec.action name: Action type: string + - jsonPath: .spec.namespace + name: Namespace + type: string - jsonPath: .spec.user name: User type: string @@ -97,20 +106,28 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: description: Operationrequest is the Schema for the operation requests API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -123,6 +140,9 @@ spec: - Update - Deprive type: string + namespace: + description: Namespace is the workspace that needs to be operated. + type: string role: enum: - Owner @@ -155,8 +175,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: users.user.sealos.io spec: group: user.sealos.io @@ -183,14 +202,19 @@ spec: description: User is the Schema for the users API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -199,12 +223,14 @@ spec: properties: csrExpirationSeconds: default: 7200 - description: "expirationSeconds is the requested duration of validity - of the issued certificate. The certificate signer may issue a certificate - with a different validity duration so a client must check the delta - between the notBefore and notAfter fields in the issued certificate - to determine the actual duration. \n The minimum valid value for - expirationSeconds is 600, i.e. 10 minutes." + description: |- + expirationSeconds is the requested duration of validity of the issued + certificate. The certificate signer may issue a certificate with a different + validity duration so a client must check the delta between the notBefore and + notAfter fields in the issued certificate to determine the actual duration. + + + The minimum valid value for expirationSeconds is 600, i.e. 10 minutes. format: int32 type: integer type: object @@ -315,7 +341,6 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: user-manager-role rules: - apiGroups: diff --git a/controllers/user/main.go b/controllers/user/main.go index f52448906a0..7aee541e0e2 100644 --- a/controllers/user/main.go +++ b/controllers/user/main.go @@ -161,13 +161,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "DeleteRequest") os.Exit(1) } - if err = (&controllers.AdaptRoleBindingReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "AdaptRoleBinding") - os.Exit(1) - } + //if err = (&controllers.AdaptRoleBindingReconciler{ + // Client: mgr.GetClient(), + // Scheme: mgr.GetScheme(), + //}).SetupWithManager(mgr); err != nil { + // setupLog.Error(err, "unable to create controller", "controller", "AdaptRoleBinding") + // os.Exit(1) + //} //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/deploy/devbox/Kubefile b/deploy/devbox/Kubefile new file mode 100644 index 00000000000..b3edf5f36b7 --- /dev/null +++ b/deploy/devbox/Kubefile @@ -0,0 +1,11 @@ +FROM scratch +COPY tars tars +COPY scripts scripts + +ENV cloudDomain=${cloudDomain:-"127.0.0.1.nip.io"} +ENV cloudPort="" +ENV registryAddr=${registryAddr:-"sealos.hub:5000"} +ENV registryUser=${registryUser:-"admin"} +ENV registryPassword=${registryPassword:-"passw0rd"} + +CMD ["bash scripts/init.sh"] diff --git a/deploy/devbox/init.sh b/deploy/devbox/init.sh new file mode 100644 index 00000000000..875547bca3d --- /dev/null +++ b/deploy/devbox/init.sh @@ -0,0 +1,33 @@ +#!/bin/bash +readonly ARCH=${1:-amd64} +set -e + +mkdir -p tars + +RetryPullImageInterval=3 +RetrySleepSeconds=3 + + +retryPullImage() { + local image=$1 + local retry=0 + local retryMax=3 + set +e + while [ $retry -lt $RetryPullImageInterval ]; do + sealos pull --policy=always --platform=linux/"${ARCH}" $image >/dev/null && break + retry=$(($retry + 1)) + echo "retry pull image $image, retry times: $retry" + sleep $RetrySleepSeconds + done + set -e + if [ $retry -eq $retryMax ]; then + echo "pull image $image failed" + exit 1 + fi +} + +retryPullImage ghcr.io/labring/sealos-cloud-devbox-controller:latest +retryPullImage ghcr.io/labring/sealos-cloud-devbox-frontend:latest + +sealos save -o tars/devbox-controller.tar ghcr.io/labring/sealos-cloud-devbox-controller:latest +sealos save -o tars/devbox-frontend.tar ghcr.io/labring/sealos-cloud-devbox-frontend:latest diff --git a/deploy/devbox/scripts/init.sh b/deploy/devbox/scripts/init.sh new file mode 100644 index 00000000000..54e779f940c --- /dev/null +++ b/deploy/devbox/scripts/init.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e + +sealos run tars/devbox-controller.tar -e cloudDomain=${cloudDomain} -e cloudPort=${cloudPort} -e registryAddr=${registryAddr} -e registryUser=${registryUser} -e registryPassword=${registryPassword} +sealos run tars/devbox-frontend.tar -e cloudDomain=${cloudDomain} -e cloudPort=${cloudPort} diff --git a/deploy/objectstorage/Kubefile b/deploy/objectstorage/Kubefile index c8de4664538..01c581d7cfe 100644 --- a/deploy/objectstorage/Kubefile +++ b/deploy/objectstorage/Kubefile @@ -11,6 +11,6 @@ ENV minioStorageSize=${minioStorageSize:-1Gi} ENV promStorageSize=${promStorageSize:-1Gi} ENV minioAdminUser=${minioAdminUser:-"username"} ENV minioAdminPassword=${minioAdminPassword:-"passw0rd"} -ENV minioKubeblocksPassword=${minioAdminPassword:-"kubeblocks"} +ENV minioKubeblocksPassword=${minioKubeblocksPassword:-"kubeblocks"} CMD ["bash scripts/init.sh"] diff --git a/deploy/objectstorage/README.md b/deploy/objectstorage/README.md index a7c9bd9c561..9e0fee22bcb 100644 --- a/deploy/objectstorage/README.md +++ b/deploy/objectstorage/README.md @@ -2,7 +2,13 @@ ## version -date: 2024.9.10 +### date: 2024.10.16 + +log: fix minioKubeblocksPassword env error + +### date: 2024.9.10 + +log: update controller and frontend image ## components diff --git a/deploy/objectstorage/init.sh b/deploy/objectstorage/init.sh index 5fbe719077d..9e1b22eba1c 100644 --- a/deploy/objectstorage/init.sh +++ b/deploy/objectstorage/init.sh @@ -1,6 +1,6 @@ #!/bin/bash set -e -export readonly ARCH=${1:-amd64} +readonly ARCH=${1:-amd64} mkdir -p tars RetryPullImageInterval=3 diff --git a/docs/5.0/i18n/zh-Hans/developer-guide/sealos/installation.md b/docs/5.0/i18n/zh-Hans/developer-guide/sealos/installation.md index bbe924dd257..6da7063433f 100644 --- a/docs/5.0/i18n/zh-Hans/developer-guide/sealos/installation.md +++ b/docs/5.0/i18n/zh-Hans/developer-guide/sealos/installation.md @@ -102,9 +102,9 @@ Sealos 需要使用证书来保证通信安全,默认在您不提供证书的 使用 nip.io 作为 Sealos 的域名非常简单,只需在第一个 Master 节点上执行以下命令,并根据提示输入参数: -```bash -$ curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.0/scripts/cloud/install.sh -o /tmp/install.sh && bash /tmp/install.sh \ - --cloud-version=v5.0.0 \ +```bash +$ curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.1/scripts/cloud/install.sh -o /tmp/install.sh && SEALOS_VERSION=v5.0.1 && bash /tmp/install.sh \ + --cloud-version=v5.0.1 \ --image-registry=registry.cn-shanghai.aliyuncs.com --zh \ --proxy-prefix=https://mirror.ghproxy.com ``` @@ -147,8 +147,8 @@ cloud.example.io A 192.168.1.10 然后在第一个 Master 节点上执行以下命令,并根据提示输入参数: ```bash -$ curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.0/scripts/cloud/install.sh -o /tmp/install.sh && bash /tmp/install.sh \ - --cloud-version=v5.0.0 \ +$ curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.1/scripts/cloud/install.sh -o /tmp/install.sh && SEALOS_VERSION=v5.0.1 && bash /tmp/install.sh \ + --cloud-version=v5.0.1 \ --image-registry=registry.cn-shanghai.aliyuncs.com --zh \ --proxy-prefix=https://mirror.ghproxy.com \ --cloud-domain= \ @@ -174,8 +174,8 @@ cloud.example.io A 192.168.1.10 然后在第一个 Master 节点上执行以下命令,并根据提示输入参数: ```bash -$ curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.0/scripts/cloud/install.sh -o /tmp/install.sh && bash /tmp/install.sh \ - --cloud-version=v5.0.0 \ +$ curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.1/scripts/cloud/install.sh -o /tmp/install.sh && SEALOS_VERSION=v5.0.1 && bash /tmp/install.sh \ + --cloud-version=v5.0.1 \ --image-registry=registry.cn-shanghai.aliyuncs.com --zh \ --proxy-prefix=https://mirror.ghproxy.com \ --cloud-domain= @@ -233,8 +233,8 @@ $ curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring 然后在第一个 Master 节点上执行以下命令,并根据提示输入参数: ```bash -$ curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.0/scripts/cloud/install.sh -o /tmp/install.sh && bash /tmp/install.sh \ - --cloud-version=v5.0.0 \ +$ curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.1/scripts/cloud/install.sh -o /tmp/install.sh && SEALOS_VERSION=v5.0.1 && bash /tmp/install.sh \ + --cloud-version=v5.0.1 \ --image-registry=registry.cn-shanghai.aliyuncs.com --zh \ --proxy-prefix=https://mirror.ghproxy.com \ --cloud-domain= @@ -355,7 +355,7 @@ Linux 不同发行版更新根证书存储的命令不一样,用来保存私 # update-ca-certificates 会添加 /etc/ca-certificates.conf 配置文件中指定的证书 # 另外所有 /usr/local/share/ca-certificates/*.crt 会被列为隐式信任 $ sudo update-ca-certificates - + # - 删除 $ sudo rm /usr/local/share/ca-certificates/root_ca.crt $ sudo update-ca-certificates --fresh @@ -431,4 +431,4 @@ Linux 不同发行版更新根证书存储的命令不一样,用来保存私 选择刚刚下载的 License 文件进行上传,然后点击右下角的「激活 License」,便可激活 License。 - ![](images/sealos-cost-center.jpg) \ No newline at end of file + ![](images/sealos-cost-center.jpg) diff --git a/docs/website/docusaurus.config.js b/docs/website/docusaurus.config.js index 19dc5010ad9..eccc6c41755 100644 --- a/docs/website/docusaurus.config.js +++ b/docs/website/docusaurus.config.js @@ -75,32 +75,32 @@ const config = { themeConfig: { // @type {import('@docusaurus/preset-classic').ThemeConfig} metadata: [{ name: 'title', content: 'Sealos by 环界云' }], - announcementBar: { - id: 'sealos_tip', - content: ` -
-
${isDomesticSite ? 'If you are an international user, please visit 👉' : '如果您是国内用户,请直接访问 👉 '}
-
- ${isDomesticSite ? 'International Site' : '国内官网'} -
- - - - - - - - - - - - -
- `, - isCloseable: true, - }, + // announcementBar: { + // id: 'sealos_tip', + // content: ` + //
+ //
${isDomesticSite ? 'If you are an international user, please visit 👉' : '如果您是国内用户,请直接访问 👉 '}
+ //
+ // ${isDomesticSite ? 'International Site' : '国内官网'} + //
+ // + // + // + // + // + // + // + // + // + // + // + // + //
+ // `, + // isCloseable: true, + // }, algolia: { // Algolia 提供的应用 ID appId: "SLTSB7B9Y0", diff --git a/docs/website/src/components/SaleBanner/index.tsx b/docs/website/src/components/SaleBanner/index.tsx index cc5c8437a32..9bd6c86363b 100644 --- a/docs/website/src/components/SaleBanner/index.tsx +++ b/docs/website/src/components/SaleBanner/index.tsx @@ -13,11 +13,8 @@ export default function SaleBanner() { setIsBannerVisible(false); }; - const goDetailFeishu = () => { - window.open( - `https://fael3z0zfze.feishu.cn/wiki/SzKowEuQji5coRkm5o8cm8oJn3L?from=from_copylink`, - '_blank' - ); + const goDetail = () => { + window.open(`https://mp.weixin.qq.com/s/jzOfviMgXD85r2zMQWskvA`, '_blank'); }; useEffect(() => { @@ -36,8 +33,8 @@ export default function SaleBanner() { return ( <>
- 🎉Sealos 6.18 福利大放送!充值优惠限时开启,多充多送还有精美周边! -
+ 🎉 1024 程序员节福利 Sealos 免费送你云资源 +
活动详情
@@ -50,13 +47,13 @@ export default function SaleBanner() { Sealos
-
🎉Sealos 6.18 福利大放送!
-
充值优惠限时开启
-
多充多送还有精美周边!
+
🎉 Sealos 免费送你云资源
+
1024 充值优惠限时开启
+
体验Devbox,即送20余额
{ - window.open(`${cloudUrl}/?openapp=system-costcenter?openRecharge=true`, '_blank'); + window.open(`${cloudUrl}/?openapp=system-devbox?openRecharge=true`, '_blank'); closeBanner(); }} > diff --git a/docs/website/src/pages/components/Header/index.scss b/docs/website/src/pages/components/Header/index.scss index e2fb98dc33c..e7485d3068c 100644 --- a/docs/website/src/pages/components/Header/index.scss +++ b/docs/website/src/pages/components/Header/index.scss @@ -6,7 +6,7 @@ justify-content: center; align-items: center; flex-direction: column; - // padding-top: 48px; // sale + padding-top: 48px; // sale .header-img { width: 100%; diff --git a/docs/website/src/pages/index.tsx b/docs/website/src/pages/index.tsx index 7e5c5ebef25..3ca262d0ba2 100644 --- a/docs/website/src/pages/index.tsx +++ b/docs/website/src/pages/index.tsx @@ -97,6 +97,7 @@ const Home = () => {
+ diff --git a/frontend/desktop/public/locales/en/common.json b/frontend/desktop/public/locales/en/common.json index 37b147b2df8..c99e8045966 100644 --- a/frontend/desktop/public/locales/en/common.json +++ b/frontend/desktop/public/locales/en/common.json @@ -133,6 +133,7 @@ "merge_account_tips2": "The account you are trying to bind has been used by another user. You can choose to merge accounts to manage your information and settings in a unified way. After the merge is completed, your private workspace may be converted into a regular workspace. Do you want to merge accounts now?", "merge_account_title": "Account has been bound", "message_center": "Message Center", + "modify_member": "Modify Member", "monitor": "Monitor", "more_apps": "More Apps", "name": "Name", diff --git a/frontend/desktop/public/locales/zh/common.json b/frontend/desktop/public/locales/zh/common.json index 55c9e609a63..da5db1fdae7 100644 --- a/frontend/desktop/public/locales/zh/common.json +++ b/frontend/desktop/public/locales/zh/common.json @@ -129,6 +129,7 @@ "merge_account_tips2": "您尝试绑定的账号已被其他用户使用。您可以选择合并账户,以统一管理您的信息和设置。 在合并完成后,个人空间有可能转化为普通的工作空间,是否现在合并账户?", "merge_account_title": "账户已被绑定", "message_center": "消息中心", + "modify_member": "修改权限", "monitor": "监控", "more_apps": "更多应用", "name": "姓名", diff --git a/frontend/desktop/src/components/team/DissolveTeam.tsx b/frontend/desktop/src/components/team/DissolveTeam.tsx index 0843eab2a18..af3d225da07 100644 --- a/frontend/desktop/src/components/team/DissolveTeam.tsx +++ b/frontend/desktop/src/components/team/DissolveTeam.tsx @@ -1,26 +1,25 @@ +import { deleteTeamRequest } from '@/api/namespace'; +import { useCustomToast } from '@/hooks/useCustomToast'; +import useSessionStore from '@/stores/session'; +import { ApiResp } from '@/types'; import { Button, - Image, + ButtonProps, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, - useDisclosure, - Text, Spinner, - ButtonProps + Text, + useDisclosure } from '@chakra-ui/react'; -import CustomInput from './Input'; -import { useState } from 'react'; -import { useCustomToast } from '@/hooks/useCustomToast'; +import { DeleteIcon } from '@sealos/ui'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { deleteTeamRequest } from '@/api/namespace'; -import useSessionStore from '@/stores/session'; -import { ApiResp } from '@/types'; import { useTranslation } from 'next-i18next'; -import { DeleteIcon } from '@sealos/ui'; +import { useState } from 'react'; +import CustomInput from './Input'; export default function DissolveTeam({ nsid, ns_uid, @@ -95,7 +94,7 @@ export default function DissolveTeam({ > - Warning + {t('common:warning')} {mutation.isLoading ? ( @@ -108,7 +107,7 @@ export default function DissolveTeam({ e.preventDefault(); setTeamName(e.target.value); }} - placeholder={t('common:name_of_team') || ''} + placeholder={t('common:team') || '' + ' ID'} value={teamName} />