diff --git a/Makefile b/Makefile index 500f9eb0d1..89fffaf45a 100644 --- a/Makefile +++ b/Makefile @@ -733,6 +733,7 @@ backend-debug: go-check generate-backend $(EXECUTABLE)-debug $(EXECUTABLE)-debug: $(GO_SOURCES) $(TAGS_PREREQ) CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $@ + .PHONY: frontend frontend: $(WEBPACK_DEST) ## build frontend files @@ -969,6 +970,13 @@ docker: # support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" . # This endif closes the if at the top of the file + +# 添加一个新目标,用于构建 controller-manager +.PHONY: controller-manager +controller-manager: go-check + @echo "Building controller-manager..." + CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o controller-manager modules/k8s/cmd/controller-manager/controller-manager.go + endif # Disable parallel execution because it would break some targets that don't diff --git a/docker/root/etc/s6/gitea/setup b/docker/root/etc/s6/gitea/setup index b801ef4e03..148c991ae1 100755 --- a/docker/root/etc/s6/gitea/setup +++ b/docker/root/etc/s6/gitea/setup @@ -32,7 +32,7 @@ if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then fi # Substitute the environment variables in the template - APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \ + APP_NAME=${APP_NAME:-"DevStar: The Last Mile of Al for R&D"} \ RUN_MODE=${RUN_MODE:-"prod"} \ DOMAIN=${DOMAIN:-"localhost"} \ SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \ diff --git a/docker/rootless/usr/local/bin/docker-setup.sh b/docker/rootless/usr/local/bin/docker-setup.sh index feab02a379..678ceb631c 100755 --- a/docker/rootless/usr/local/bin/docker-setup.sh +++ b/docker/rootless/usr/local/bin/docker-setup.sh @@ -26,7 +26,7 @@ if [ ! -f ${GITEA_APP_INI} ]; then fi # Substitute the environment variables in the template - APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \ + APP_NAME=${APP_NAME:-"DevStar: The Last Mile of Al for R&D"} \ RUN_MODE=${RUN_MODE:-"prod"} \ RUN_USER=${USER:-"git"} \ SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \ diff --git a/docs/devcontainer-local-k8s.md b/docs/devcontainer-local-k8s.md new file mode 100644 index 0000000000..ad7fc47ec6 --- /dev/null +++ b/docs/devcontainer-local-k8s.md @@ -0,0 +1,280 @@ +## DevContainer(Kubernetes + Istio)本地运行 + +仅保留三点:Istio 1.27.1 安装、app.ini 手动配置、安装 Devcontainer CRD(内嵌 YAML)。 + +### 1) 安装 Istio 1.27.1(指定版本) + +```bash +ISTIO_VER=1.27.1 +curl -L https://istio.io/downloadIstio | ISTIO_VERSION=${ISTIO_VER} sh - +export PATH="$PWD/istio-${ISTIO_VER}/bin:$PATH" + +istioctl x precheck +istioctl install -y --set profile=default + +kubectl -n istio-system get svc istio-ingressgateway -o wide +``` + +说明:本项目 WebTerminal 使用 HTTP/80,经 `istio-ingressgateway` 进入,无需立刻配置 HTTPS。 + +### 2) 本地 app.ini 手动配置(非helm安装的 devstar 不会自动写入) + +文件:`devstar/custom/conf/app.ini` + +```ini + +[devstar.devcontainer] +NAMESPACE = default # 创建的devcontainer所在的命名空间 +HOST = 192.168.23.138 # 和[server].DOMAIN一致 +``` + +保存后重启后端以加载配置。 + +### 3) 安装 Devcontainer CRD(内嵌 YAML,可直接 apply) + +可直接复制以下清单,通过标准输入安装: + +```bash +kubectl apply -f - <<'YAML' +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: devcontainerapps.devcontainer.devstar.cn +spec: + group: devcontainer.devstar.cn + names: + kind: DevcontainerApp + listKind: DevcontainerAppList + plural: devcontainerapps + singular: devcontainerapp + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: DevcontainerApp is the Schema for the devcontainerapps 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: DevcontainerAppSpec defines the desired state of DevcontainerApp + properties: + failedJobsHistoryLimit: + description: |- + The number of failed finished jobs to retain. + This is a pointer to distinguish between explicit zero and not specified. + format: int32 + minimum: 0 + type: integer + service: + description: ServiceSpec specifies Service for DevContainer + properties: + extraPorts: + description: ExtraPorts 定义额外的端口配置 + items: + description: ExtraPortSpec 定义额外端口配置 + properties: + containerPort: + description: ContainerPort 是容器内的端口号 + maximum: 65535 + minimum: 1 + type: integer + name: + description: Name 是端口的名称 + type: string + servicePort: + description: ServicePort 是服务暴露的端口号 + maximum: 65535 + minimum: 1 + type: integer + required: + - containerPort + - servicePort + type: object + type: array + nodePort: + maximum: 32767 + minimum: 30000 + type: integer + servicePort: + minimum: 1 + type: integer + type: object + startingDeadlineSeconds: + description: |- + Optional deadline in seconds for starting the job if it misses scheduled + time for any reason. Missed jobs executions will be counted as failed ones. + format: int64 + minimum: 0 + type: integer + statefulset: + description: StatefulSetSpec specifies StatefulSet for DevContainer + properties: + command: + items: + type: string + type: array + containerPort: + minimum: 1 + type: integer + gitRepositoryURL: + type: string + image: + type: string + sshPublicKeyList: + description: 至少包含一个 SSH Public Key 才能通过校验规则 + items: + type: string + minItems: 1 + type: array + required: + - command + - gitRepositoryURL + - image + - sshPublicKeyList + type: object + successfulJobsHistoryLimit: + description: |- + The number of successful finished jobs to retain. + This is a pointer to distinguish between explicit zero and not specified. + format: int32 + minimum: 0 + type: integer + suspend: + description: |- + This flag tells the controller to suspend subsequent executions, it does + not apply to already started executions. Defaults to false. + type: boolean + required: + - statefulset + type: object + status: + description: DevcontainerAppStatus defines the observed state of DevcontainerApp + properties: + active: + description: A list of pointers to currently running jobs. + items: + description: ObjectReference contains enough information to let + you inspect or modify the referred object. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + type: array + extraPortsAssigned: + description: ExtraPortsAssigned 存储额外端口映射的 NodePort + items: + description: ExtraPortAssigned 定义已分配的额外端口信息 + properties: + containerPort: + description: ContainerPort 是容器内的端口号 + type: integer + name: + description: Name 是端口的名称 + type: string + nodePort: + description: NodePort 是 Kubernetes 分配的 NodePort + type: integer + servicePort: + description: ServicePort 是服务暴露的端口号 + type: integer + required: + - containerPort + - nodePort + - servicePort + type: object + type: array + lastScheduleTime: + description: Information when was the last time the job was successfully + scheduled. + format: date-time + type: string + nodePortAssigned: + description: NodePortAssigned 存储 DevcontainerApp CRD调度后集群分配的 NodePort + type: integer + ready: + description: Ready 标识 DevcontainerApp 管理的 Pod 的 Readiness Probe 是否达到就绪状态 + type: boolean + type: object + type: object + served: true + storage: true + subresources: + status: {} +YAML +``` + +验证 CRD: + +```bash +kubectl get crd devcontainerapps.devcontainer.devstar.cn -o wide +``` + +### 4) 编译与运行 controller-manager(必需) + +controller-manager 负责监听 `DevcontainerApp` CR,并创建/更新/删除底层 K8s 资源(StatefulSet、Service 等),并回写 `Status.Ready` 等状态。仅安装 CRD 不会触发任何实际资源变更,必须运行 controller-manager 才会生效。 + +- 在主目录编译并运行: + +```bash +cd /home/psx/devstar-main +make controller-manager +./controller-manager +# 观察日志:应能看到 Reconcile 日志;创建 DevcontainerApp 后会创建 sts/svc +``` + diff --git a/go.mod b/go.mod index 366b179476..aeb9a3581c 100644 --- a/go.mod +++ b/go.mod @@ -96,6 +96,8 @@ require ( github.com/nektos/act v0.2.63 github.com/niklasfasching/go-org v1.8.0 github.com/olivere/elastic/v7 v7.0.32 + github.com/onsi/ginkgo/v2 v2.22.0 + github.com/onsi/gomega v1.36.1 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 github.com/pkg/errors v0.9.1 @@ -107,7 +109,7 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sassoftware/go-rpmutils v0.4.0 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 - github.com/spf13/cobra v1.8.1 + github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.0 @@ -129,52 +131,75 @@ require ( golang.org/x/sync v0.15.0 golang.org/x/sys v0.33.0 golang.org/x/text v0.26.0 - google.golang.org/grpc v1.72.0 + google.golang.org/grpc v1.72.1 google.golang.org/protobuf v1.36.6 gopkg.in/ini.v1 v1.67.0 gopkg.in/yaml.v3 v3.0.1 - istio.io/api v1.26.3 - istio.io/client-go v1.26.3 - k8s.io/api v0.33.3 - k8s.io/apimachinery v0.33.3 - k8s.io/client-go v0.33.3 - k8s.io/component-base v0.33.3 + istio.io/api v1.27.2 + istio.io/client-go v1.27.2 + k8s.io/api v0.34.1 + k8s.io/apimachinery v0.34.1 + k8s.io/client-go v0.34.1 + k8s.io/component-base v0.34.1 k8s.io/klog/v2 v2.130.1 + k8s.io/kubectl v0.34.1 mvdan.cc/xurls/v2 v2.6.0 - sigs.k8s.io/controller-runtime v0.21.0 + sigs.k8s.io/controller-runtime v0.22.3 + sigs.k8s.io/yaml v1.6.0 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 xorm.io/builder v0.3.13 xorm.io/xorm v1.3.9 ) require ( + cel.dev/expr v0.24.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect - github.com/distribution/reference v0.5.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // 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/gogo/protobuf v1.3.2 // indirect - github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/cel-go v0.26.0 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/moby/spdystream v0.5.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/term v0.32.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + k8s.io/apiextensions-apiserver v0.34.1 // indirect + k8s.io/apiserver v0.34.1 // indirect + k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect + k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) require ( - cel.dev/expr v0.20.0 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect @@ -187,7 +212,6 @@ require ( github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect - github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect @@ -195,7 +219,6 @@ require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.22.0 // indirect - github.com/blang/semver/v4 v4.0.0 // indirect github.com/blevesearch/bleve_index_api v1.2.8 // indirect github.com/blevesearch/geo v0.2.0 // indirect github.com/blevesearch/go-faiss v1.0.25 // indirect @@ -217,7 +240,6 @@ require ( github.com/boombuler/barcode v1.0.2 // indirect github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect github.com/caddyserver/zerossl v0.1.3 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect @@ -225,7 +247,7 @@ require ( github.com/couchbase/go-couchbase v0.1.1 // indirect github.com/couchbase/gomemcached v0.3.3 // indirect github.com/couchbase/goutils v0.1.2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect @@ -233,10 +255,8 @@ require ( github.com/dlclark/regexp2 v1.11.5 // indirect github.com/docker/docker v24.0.9+incompatible github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect - github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/color v1.18.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fxamacker/cbor/v2 v2.8.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect @@ -244,7 +264,6 @@ require ( github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-logr/stdr v1.2.2 // indirect github.com/go-webauthn/x v0.1.20 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect @@ -254,19 +273,16 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.23.2 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.3 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect @@ -282,9 +298,8 @@ require ( github.com/minio/crc64nvme v1.0.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -315,16 +330,16 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/zeebo/assert v1.3.0 // indirect github.com/zeebo/blake3 v0.2.4 // indirect - go.etcd.io/bbolt v1.4.0 // indirect + go.etcd.io/bbolt v1.4.2 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect - go.opentelemetry.io/proto/otlp v1.4.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect @@ -332,14 +347,9 @@ require ( golang.org/x/mod v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect golang.org/x/tools v0.33.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.33.0 // indirect - k8s.io/apiserver v0.33.0 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect ) replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 @@ -361,3 +371,7 @@ exclude github.com/gofrs/uuid v4.0.0+incompatible exclude github.com/goccy/go-json v0.4.11 exclude github.com/satori/go.uuid v1.2.0 + +replace github.com/docker/distribution => github.com/distribution/distribution v2.8.3+incompatible + +replace github.com/distribution/reference => github.com/distribution/reference v0.5.0 diff --git a/go.sum b/go.sum index 0dd6e1ae05..dd16688134 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI= -cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= code.gitea.io/actions-proto-go v0.4.1 h1:l0EYhjsgpUe/1VABo2eK7zcoNX2W44WOnb0MSLrKfls= @@ -233,9 +233,8 @@ github.com/couchbase/goutils v0.1.2 h1:gWr8B6XNWPIhfalHNog3qQKfGiYyh4K4VhO3P2o9B github.com/couchbase/goutils v0.1.2/go.mod h1:h89Ek/tiOxxqjz30nPPlwZdQbdB8BwgnuBxeoUe/ViE= github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +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.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -251,6 +250,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21 h1:PdsjTl0Cg+ZJgOx/CFV5NNgO1ThTreqdgKYiDCMHJwA= github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21/go.mod h1:xJvkyD6Y2rZapGvPJLYo9dyx1s5dxBEDPa8T3YTuOk0= +github.com/distribution/distribution v2.8.3+incompatible h1:RlpEXBLq/WPXYvBYMDAmBX/SnhD67qwtvW/DzKc8pAo= +github.com/distribution/distribution v2.8.3+incompatible/go.mod h1:EgLm2NgWtdKgzF9NpMzUKgzmR7AMmb0VQi2B+ZzDRjc= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/buffer v1.1.0/go.mod h1:VwN8VdFkMY0DCALdY8o00d3IZ6Amz/UNVMWcSaJT44o= @@ -263,8 +264,6 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 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/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -289,8 +288,8 @@ github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTe github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk= github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= -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/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/ethantkoenig/rupture v1.0.1 h1:6aAXghmvtnngMgQzy7SMGdicMvkV86V4n9fT0meE5E4= @@ -311,8 +310,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 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.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= -github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0= github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= @@ -379,7 +378,6 @@ github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkv github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ= github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 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= @@ -432,19 +430,18 @@ github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= -github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= +github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= +github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= 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.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/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/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= @@ -484,8 +481,10 @@ github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pw github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= 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= @@ -609,13 +608,16 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 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/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM= @@ -627,6 +629,8 @@ github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE= github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0= 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/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= github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY= github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= @@ -731,11 +735,10 @@ github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:s github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -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/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= @@ -823,28 +826,28 @@ github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l gitlab.com/gitlab-org/api/client-go v0.127.0 h1:8xnxcNKGF2gDazEoMs+hOZfOspSSw8D0vAoWhQk9U+U= gitlab.com/gitlab-org/api/client-go v0.127.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= +go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I= +go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= 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.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +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.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= +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.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= -go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= +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/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -856,6 +859,10 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +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-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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= @@ -1009,8 +1016,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1: google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= -google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= +google.golang.org/grpc v1.72.1/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= @@ -1049,28 +1056,30 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -istio.io/api v1.26.3 h1:/TiA7bJi24yBQSgpLy5vHhFkobf4DWS1L+CuUxNk4os= -istio.io/api v1.26.3/go.mod h1:DTVGH6CLXj5W8FF9JUD3Tis78iRgT1WeuAnxfTz21Wg= -istio.io/client-go v1.26.3 h1:ryF4+Nyz5wDO4mVCzXcm2W+fqbnekY88Z36hTcv5fnw= -istio.io/client-go v1.26.3/go.mod h1:u2p5L7UvjNswrrlHZ+QMlUOjERK2sXputywzyNhtTMg= -k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= -k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= -k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= -k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= -k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= -k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc= -k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8= -k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= -k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= -k8s.io/component-base v0.33.3 h1:mlAuyJqyPlKZM7FyaoM/LcunZaaY353RXiOd2+B5tGA= -k8s.io/component-base v0.33.3/go.mod h1:ktBVsBzkI3imDuxYXmVxZ2zxJnYTZ4HAsVj9iF09qp4= +istio.io/api v1.27.2 h1:t0m2EAT+LWGA/jSvsyxEhGQoIQYdXD5sJG7tQ9OtQk0= +istio.io/api v1.27.2/go.mod h1:DTVGH6CLXj5W8FF9JUD3Tis78iRgT1WeuAnxfTz21Wg= +istio.io/client-go v1.27.2 h1:4IsF7UAdV5Yg0iq6ONyWZpjFr3z2ahkIbLWyzOHCAwA= +istio.io/client-go v1.27.2/go.mod h1:zgT5R1USl6rwYK1eb2kisPuiji05TQJE7CQHU253iAg= +k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= +k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= +k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= +k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= +k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= +k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= +k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= +k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= +k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= +k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= 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-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= +k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI= +k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= @@ -1097,17 +1106,16 @@ pgregory.net/rapid v0.4.2 h1:lsi9jhvZTYvzVpeG93WWgimPRmiJQfGFRNTEZh1dtY0= pgregory.net/rapid v0.4.2/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y= +sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo= diff --git a/models/user/user.go b/models/user/user.go index 1c51348b28..3a5fc4ae68 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -125,6 +125,7 @@ type User struct { AllowImportLocal bool // Allow migrate repository by local path AllowCreateOrganization bool `xorm:"DEFAULT true"` AllowCreateDevcontainer bool `xorm:"DEFAULT false"` + AllowCreateActRunner bool `xorm:"DEFAULT false"` // true: the user is not allowed to log in Web UI. Git/SSH access could still be allowed (please refer to Git/SSH access related code/documents) ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"` @@ -274,6 +275,11 @@ func (u *User) CanCreateDevcontainer() bool { return u.AllowCreateDevcontainer } +// CanCreateActrunner returns true if user can create organisation. +func (u *User) CanCreateActrunner() bool { + return u.AllowCreateActRunner +} + // CanEditGitHook returns true if user can edit Git hooks. func (u *User) CanEditGitHook() bool { return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook) @@ -640,6 +646,7 @@ type CreateUserOverwriteOptions struct { Visibility *structs.VisibleType AllowCreateOrganization optional.Option[bool] AllowCreateDevcontainer optional.Option[bool] + AllowCreateActRunner optional.Option[bool] EmailNotificationsPreference *string MaxRepoCreation *int Theme *string @@ -668,6 +675,7 @@ func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, o u.Visibility = setting.Service.DefaultUserVisibilityMode u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation u.AllowCreateDevcontainer = setting.Service.DefaultAllowCreateDevcontainer + u.AllowCreateActRunner = setting.Service.DefaultAllowCreateActRunner u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification u.MaxRepoCreation = -1 u.Theme = setting.UI.DefaultTheme diff --git a/models/user/user_system.go b/models/user/user_system.go index cc7068551a..b28718cd65 100644 --- a/models/user/user_system.go +++ b/models/user/user_system.go @@ -59,6 +59,7 @@ func NewActionsUser() *User { Type: UserTypeBot, AllowCreateOrganization: true, AllowCreateDevcontainer: false, + AllowCreateActRunner: false, Visibility: structs.VisibleTypePublic, } } diff --git a/modules/k8s/api/application/v1/application_types.go b/modules/k8s/api/application/v1/application_types.go index 5ec8e109c8..716b01a4f6 100644 --- a/modules/k8s/api/application/v1/application_types.go +++ b/modules/k8s/api/application/v1/application_types.go @@ -1,10 +1,3 @@ -/* - * Copyright (c) Mengning Software. 2025. All rights reserved. - * Authors: DevStar Team, panshuxiao - * Create: 2025-11-19 - * Description: Application CRD Go type definitions. - */ - /* Copyright 2025. diff --git a/modules/k8s/controller/devcontainer/devcontainerapp_controller_test.go b/modules/k8s/controller/devcontainer/devcontainerapp_controller_test.go new file mode 100644 index 0000000000..f4eceb405b --- /dev/null +++ b/modules/k8s/controller/devcontainer/devcontainerapp_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 devcontainer + +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" + + devcontainerv1 "code.gitea.io/gitea/modules/k8s/api/devcontainer/v1" +) + +var _ = Describe("DevcontainerApp 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 + } + devcontainerapp := &devcontainerv1.DevcontainerApp{} + + BeforeEach(func() { + By("creating the custom resource for the Kind DevcontainerApp") + err := k8sClient.Get(ctx, typeNamespacedName, devcontainerapp) + if err != nil && errors.IsNotFound(err) { + resource := &devcontainerv1.DevcontainerApp{ + 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 := &devcontainerv1.DevcontainerApp{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance DevcontainerApp") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &DevcontainerAppReconciler{ + 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/modules/k8s/controller/devcontainer/suite_test.go b/modules/k8s/controller/devcontainer/suite_test.go new file mode 100644 index 0000000000..5d08214fb3 --- /dev/null +++ b/modules/k8s/controller/devcontainer/suite_test.go @@ -0,0 +1,96 @@ +/* +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 devcontainer + +import ( + "context" + "fmt" + "path/filepath" + "runtime" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + devcontainerv1 "code.gitea.io/gitea/modules/k8s/api/devcontainer/v1" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment +var ctx context.Context +var cancel context.CancelFunc + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + + // The BinaryAssetsDirectory is only required if you want to run the tests directly + // without call the makefile target test. If not informed it will look for the + // default path defined in controller-runtime which is /usr/local/kubebuilder/. + // Note that you must have the required binaries setup under the bin directory to perform + // the tests directly. When we run make test it will be setup and used automatically. + BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", + fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = devcontainerv1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/modules/k8s/controller/options/options.go b/modules/k8s/controller/options/options.go new file mode 100644 index 0000000000..a6641a12b8 --- /dev/null +++ b/modules/k8s/controller/options/options.go @@ -0,0 +1,10 @@ +/* + * Please refer to the LICENSE file in the root directory of the project. + */ + +package options + +// Options 包含所有控制器可能需要的选项 +type Options struct { + // 可以根据实际需求扩展更多选项 +} diff --git a/modules/k8s/k8s.go b/modules/k8s/k8s.go index 3d5961d3ec..1741f9cb58 100644 --- a/modules/k8s/k8s.go +++ b/modules/k8s/k8s.go @@ -17,9 +17,7 @@ import ( "code.gitea.io/gitea/modules/setting" k8s_api_v1 "code.gitea.io/gitea/modules/k8s/api/devcontainer/v1" - - devcontainer_errors "code.gitea.io/gitea/modules/k8s/errors" - devcontainer_k8s_agent_modules_errors "code.gitea.io/gitea/modules/k8s/errors" + k8sErrors "code.gitea.io/gitea/modules/k8s/errors" apimachinery_api_metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apimachinery_apis_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" apimachinery_apis_v1_unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -181,7 +179,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts // 0. 检查参数 if ctx == nil || opts == nil || len(opts.Namespace) == 0 || len(opts.Name) == 0 { - return nil, devcontainer_errors.ErrIllegalDevcontainerParameters{ + return nil, k8sErrors.ErrIllegalDevcontainerParameters{ FieldList: []string{"ctx", "opts", "opts.Name", "opts.Namespace"}, Message: "cannot be nil", } @@ -190,7 +188,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts // 1. 获取 k8s CRD 资源 DevcontainerApp devcontainerUnstructured, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Get(ctx, opts.Name, opts.GetOptions) if err != nil { - return nil, devcontainer_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "Get DevcontainerApp thru k8s API Server", Message: err.Error(), } @@ -200,7 +198,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts devcontainerApp := &k8s_api_v1.DevcontainerApp{} err = apimachinery_runtime_utils.DefaultUnstructuredConverter.FromUnstructured(devcontainerUnstructured.Object, &devcontainerApp) if err != nil { - return nil, devcontainer_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "Convert k8s API Server unstructured response into DevcontainerApp", Message: err.Error(), } @@ -210,7 +208,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts if !IsK8sDevcontainerStatusReady(&devcontainerApp.Status) { // 3.1 检查 Wait 参数,若用户不需要阻塞式等待,直接返回 “DevContainer 未就绪” 错误 if opts.Wait == false { - return nil, devcontainer_errors.ErrK8sDevcontainerNotReady{ + return nil, k8sErrors.ErrK8sDevcontainerNotReady{ Name: opts.Name, Namespace: opts.Namespace, Wait: opts.Wait, @@ -220,7 +218,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts // 3.2 执行阻塞式等待 devcontainerStatusVO, err := waitUntilDevcontainerReadyWithTimeout(ctx, client, opts) if err != nil { - return nil, devcontainer_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "wait for k8s DevContainer to be ready", Message: err.Error(), } @@ -238,21 +236,21 @@ func waitUntilDevcontainerReadyWithTimeout(ctx context.Context, client dynamic_c // 0. 检查参数 if ctx == nil || client == nil || opts == nil || len(opts.Name) == 0 || len(opts.Namespace) == 0 { - return nil, devcontainer_errors.ErrIllegalDevcontainerParameters{ + return nil, k8sErrors.ErrIllegalDevcontainerParameters{ FieldList: []string{"ctx", "client", "opts", "opts.Name", "opts.Namespace"}, Message: "could not be nil", } } // 1. 注册 watcher 监听 DevContainer Status 变化 - watcherTimeoutSeconds := setting.DevContainerConfig.TimeoutSeconds + watcherTimeoutSeconds := int64(120) watcher, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Watch(ctx, apimachinery_apis_v1.ListOptions{ FieldSelector: fmt.Sprintf("metadata.name=%s", opts.Name), Watch: true, TimeoutSeconds: &watcherTimeoutSeconds, }) if err != nil { - return nil, devcontainer_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "register watcher of DevContainer Readiness", Message: err.Error(), } @@ -288,7 +286,7 @@ func waitUntilDevcontainerReadyWithTimeout(ctx context.Context, client dynamic_c // 2.3 监听 DevcontainerApp ERROR 事件,返回报错信息 apimachineryApiMetav1Status, ok := event.Object.(*apimachinery_api_metav1.Status) if !ok { - return nil, devcontainer_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: fmt.Sprintf("wait for Devcontainer '%s' in namespace '%s' to be ready", opts.Name, opts.Namespace), Message: fmt.Sprintf("An error occurred in k8s CRD DevcontainerApp Watcher: \n"+ " Code: %v (status = %v)\n"+ @@ -303,7 +301,7 @@ func waitUntilDevcontainerReadyWithTimeout(ctx context.Context, client dynamic_c } case apimachinery_watch.Deleted: // 2.4 监听 DevcontainerApp DELETED 事件,返回报错信息 - return nil, devcontainer_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: fmt.Sprintf("Open DevContainer '%s' in namespace '%s'", opts.Name, opts.Namespace), Message: fmt.Sprintf("'%s' of Kind DevcontainerApp has been Deleted", opts.Name), } @@ -311,7 +309,7 @@ func waitUntilDevcontainerReadyWithTimeout(ctx context.Context, client dynamic_c } // 3. k8s CRD DevcontainerApp Watcher 超时关闭处理:直接返回超时错误 - return nil, devcontainer_errors.ErrOpenDevcontainerTimeout{ + return nil, k8sErrors.ErrOpenDevcontainerTimeout{ Name: opts.Name, Namespace: opts.Namespace, TimeoutSeconds: setting.DevContainerConfig.TimeoutSeconds, @@ -357,7 +355,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op jsonData, err := json.Marshal(devcontainerApp) if err != nil { log.Error("Failed to marshal DevcontainerApp to JSON: %v", err) - return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "Marshal JSON", Message: err.Error(), } @@ -371,7 +369,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op err = unstructuredObj.UnmarshalJSON(jsonData) if err != nil { log.Error("Failed to unmarshal JSON to Unstructured: %v", err) - return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "Unmarshal JSON to Unstructured", Message: err.Error(), } @@ -386,7 +384,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op result, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Create(ctx, unstructuredObj, opts.CreateOptions) if err != nil { log.Error("Failed to create DevcontainerApp: %v", err) - return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "create DevContainer via Dynamic Client", Message: err.Error(), } @@ -398,7 +396,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op resultJSON, err := result.MarshalJSON() if err != nil { log.Error("Failed to marshal result to JSON: %v", err) - return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "Marshal result JSON", Message: err.Error(), } @@ -407,7 +405,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op createdDevcontainer := &k8s_api_v1.DevcontainerApp{} if err := json.Unmarshal(resultJSON, createdDevcontainer); err != nil { log.Error("Failed to unmarshal result JSON: %v", err) - return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "Unmarshal result JSON", Message: err.Error(), } @@ -418,7 +416,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op func DeleteDevcontainer(ctx context.Context, client dynamic_client.Interface, opts *DeleteDevcontainerOptions) error { if ctx == nil || opts == nil || len(opts.Namespace) == 0 || len(opts.Name) == 0 { - return devcontainer_errors.ErrIllegalDevcontainerParameters{ + return k8sErrors.ErrIllegalDevcontainerParameters{ FieldList: []string{"ctx", "opts", "opts.Name", "opts.Namespace"}, Message: "cannot be nil", } @@ -427,7 +425,7 @@ func DeleteDevcontainer(ctx context.Context, client dynamic_client.Interface, op err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Delete(ctx, opts.Name, opts.DeleteOptions) if err != nil { log.Warn("Failed to delete DevcontainerApp '%s' in namespace '%s': %s", opts.Name, opts.Namespace, err.Error()) - return devcontainer_errors.ErrOperateDevcontainer{ + return k8sErrors.ErrOperateDevcontainer{ Action: fmt.Sprintf("delete devcontainer '%s' in namespace '%s'", opts.Name, opts.Namespace), Message: err.Error(), } @@ -439,7 +437,7 @@ func DeleteDevcontainer(ctx context.Context, client dynamic_client.Interface, op func ListDevcontainers(ctx context.Context, client dynamic_client.Interface, opts *ListDevcontainersOptions) (*k8s_api_v1.DevcontainerAppList, error) { if ctx == nil || opts == nil || len(opts.Namespace) == 0 { - return nil, devcontainer_errors.ErrIllegalDevcontainerParameters{ + return nil, k8sErrors.ErrIllegalDevcontainerParameters{ FieldList: []string{"ctx", "namespace"}, Message: "cannot be empty", } @@ -447,7 +445,7 @@ func ListDevcontainers(ctx context.Context, client dynamic_client.Interface, opt list, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).List(ctx, opts.ListOptions) if err != nil { - return nil, devcontainer_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: fmt.Sprintf("List Devcontainer in namespace '%s'", opts.Namespace), Message: err.Error(), } @@ -455,14 +453,14 @@ func ListDevcontainers(ctx context.Context, client dynamic_client.Interface, opt // JSON 反序列化为 DevcontainerAppList jsonData, err := list.MarshalJSON() if err != nil { - return nil, devcontainer_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "verify JSON data of Devcontainer List", Message: err.Error(), } } devcontainerList := &k8s_api_v1.DevcontainerAppList{} if err := json.Unmarshal(jsonData, devcontainerList); err != nil { - return nil, devcontainer_errors.ErrOperateDevcontainer{ + return nil, k8sErrors.ErrOperateDevcontainer{ Action: "deserialize Devcontainer List data", Message: err.Error(), } diff --git a/modules/setting/service.go b/modules/setting/service.go index e31b1ff9dd..991f18e130 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -72,6 +72,7 @@ var Service = struct { DefaultKeepEmailPrivate bool DefaultAllowCreateOrganization bool DefaultAllowCreateDevcontainer bool + DefaultAllowCreateActRunner bool DefaultUserIsRestricted bool EnableTimetracking bool DefaultEnableTimetracking bool @@ -207,6 +208,7 @@ func loadServiceFrom(rootCfg ConfigProvider) { Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool() Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true) Service.DefaultAllowCreateDevcontainer = sec.Key("DEFAULT_ALLOW_CREATE_DEVCONTAINER").MustBool(true) + Service.DefaultAllowCreateActRunner = sec.Key("DEFAULT_ALLOW_CREATE_ACTRUNNER").MustBool(false) Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false) Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true) if Service.EnableTimetracking { diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index 3b4ef8b50b..e0cdd081e3 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -55,6 +55,7 @@ type EditUserOption struct { ProhibitLogin *bool `json:"prohibit_login"` AllowCreateOrganization *bool `json:"allow_create_organization"` AllowCreateDevcontainer *bool `json:"allow_create_devcontainer"` + AllowCreateActRunner *bool `json:"allow_create_actrunner"` Restricted *bool `json:"restricted"` Visibility string `json:"visibility" binding:"In(,public,limited,private)"` } diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 4fb5c644b1..7fa5effb21 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -245,8 +245,8 @@ license_desc = Go get install=Instalace installing_desc=Probíhá instalace, čekejte prosím... title=Výchozí konfigurace -docker_helper=Pokud spouštíte Gitea v Dockeru, přečtěte si dokumentaci, než budete měnit jakákoliv nastavení. -require_db_desc=Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). +k8s_helper=Pokud spouštíte DevStar v Kubernetesu, přečtěte si dokumentaci, než budete měnit jakákoliv nastavení. +require_db_desc=DevStar requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). db_title=Nastavení databáze db_type=Typ databáze host=Hostitel @@ -351,7 +351,7 @@ password_algorithm=Hash algoritmus hesla invalid_password_algorithm=Neplatný algoritmus hash hesla password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a sílu. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy. enable_update_checker=Povolit kontrolu aktualizací -enable_update_checker_helper=Kontroluje vydání nových verzí pravidelně připojením ke gitea.io. +enable_update_checker_helper=Kontroluje vydání nových verzí pravidelně připojením ke devstar.cn. env_config_keys=Konfigurace prostředí env_config_keys_prompt=Následující proměnné prostředí budou také použity pro váš konfigurační soubor: config_write_file_prompt=Tyto možnosti konfigurace budou zapsány do: %s diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 7c3cc71dda..ec41621af2 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -251,8 +251,8 @@ license_desc = Go get install=Installation installing_desc=Wird jetzt installiert, bitte warten... title=Erstkonfiguration -docker_helper=Wenn du Gitea in einem Docker-Container nutzt, lies bitte die Dokumentation, bevor du irgendwelche Einstellungen veränderst. -require_db_desc=Gitea benötigt MySQL, PostgreSQL, MSSQL, SQLite3 oder TiDB (MySQL-Protokoll). +k8s_helper=Wenn du DevStar in einem Kubernetes-Container nutzt, lies bitte die Dokumentation, bevor du irgendwelche Einstellungen veränderst. +require_db_desc=DevStar benötigt MySQL, PostgreSQL, MSSQL, SQLite3 oder TiDB (MySQL-Protokoll). db_title=Datenbankeinstellungen db_type=Datenbanktyp host=Host @@ -357,7 +357,7 @@ password_algorithm=Passwort Hashing Algorithmus invalid_password_algorithm=Ungültiger Passwort-Hash-Algorithmus password_algorithm_helper=Lege einen Passwort-Hashing-Algorithmus fest. Algorithmen haben unterschiedliche Anforderungen und Stärken. Der argon2-Algorithmus ist ziemlich sicher, aber er verbraucht viel Speicher und kann für kleine Systeme ungeeignet sein. enable_update_checker=Aktualisierungsprüfung aktivieren -enable_update_checker_helper=Stellt regelmäßig eine Verbindung zu gitea.io her, um nach neuen Versionen zu prüfen. +enable_update_checker_helper=Stellt regelmäßig eine Verbindung zu devstar.cn her, um nach neuen Versionen zu prüfen. env_config_keys=Umgebungskonfiguration env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Konfigurationsdatei angewendet: config_write_file_prompt=Diese Konfigurationsoptionen werden in %s geschrieben diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index c620c0a17a..2081b0ea6a 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -203,8 +203,8 @@ license_desc = Go get [install] install=Εγκατάσταση title=Αρχικές Ρυθμίσεις -docker_helper=Αν εκτελέσετε το Gitea μέσα στο Docker, παρακαλώ διαβάστε την τεκμηρίωση πριν αλλάξετε τις ρυθμίσεις. -require_db_desc=Το Gitea απαιτεί MySQL, PostgreSQL, MSSQL, SQLite3 ή TiDB (με πρωτόκολλο MySQL). +k8s_helper=Αν εκτελέσετε το DevStar μέσα στο Kubernetes, παρακαλώ διαβάστε την τεκμηρίωση πριν αλλάξετε τις ρυθμίσεις. +require_db_desc=Το DevStar απαιτεί MySQL, PostgreSQL, MSSQL, SQLite3 ή TiDB (με πρωτόκολλο MySQL). db_title=Ρυθμίσεις Βάσης Δεδομένων db_type=Τύπος της Βάσης Δεδομένων host=Διακομιστής @@ -308,7 +308,7 @@ password_algorithm=Αλγόριθμος Hash Κωδικού Πρόσβασης invalid_password_algorithm=Μη έγκυρος αλγόριθμος κωδικού πρόσβασης password_algorithm_helper=Ορίστε τον αλγόριθμο κατακερματισμού για το κωδικό πρόσβασης. Οι αλγόριθμοι διαφέρουν σε απαιτήσεις και αντοχή. Ο αλγόριθμος argon2 είναι αρκετά ασφαλής, αλλά χρησιμοποιεί πολλή μνήμη και μπορεί να είναι ακατάλληλος για μικρά συστήματα. enable_update_checker=Ενεργοποίηση Ελεγκτή Ενημερώσεων -enable_update_checker_helper=Ελέγχει περιοδικά για νέες εκδόσεις κάνοντας σύνδεση στο gitea.io. +enable_update_checker_helper=Ελέγχει περιοδικά για νέες εκδόσεις κάνοντας σύνδεση στο devstar.cn. env_config_keys=Ρυθμίσεις Περιβάλλοντος env_config_keys_prompt=Οι ακόλουθες μεταβλητές περιβάλλοντος θα εφαρμοστούν επίσης στο αρχείο ρυθμίσεων σας: diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 95ee80b42d..2cba991984 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -256,8 +256,8 @@ license_desc = Go get install = Installation installing_desc = Installing now, please wait... title = Initial Configuration -docker_helper = If you run Gitea inside Docker, please read the documentation before changing any settings. -require_db_desc = Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). +k8s_helper = If you run DevStar inside Kubernetes, please read the documentation before changing any settings. +require_db_desc = DevStar requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). db_title = Database Settings db_type = Database Type host = Host @@ -363,8 +363,10 @@ default_keep_email_private = Hide Email Addresses by Default default_keep_email_private_popup = Hide email addresses of new user accounts by default. default_allow_create_organization = Allow Creation of Organizations by Default default_allow_create_devcontainer = Allow Creation of DevContainers by Default +default_allow_create_actrunner = Allow Creation of ActionRunners by Default default_allow_create_organization_popup = Allow new user accounts to create organizations by default. default_allow_create_devcontainer_popup = Allow new user accounts to create devcontainers by default. +default_allow_create_actrunner_popup = Allow new user accounts to create ActionRunner by default. default_enable_timetracking = Enable Time Tracking by Default default_enable_timetracking_popup = Enable time tracking for new repositories by default. no_reply_address = Hidden Email Domain @@ -373,7 +375,7 @@ password_algorithm = Password Hash Algorithm invalid_password_algorithm = Invalid password hash algorithm password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems. enable_update_checker = Enable Update Checker -enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io. +enable_update_checker_helper = Checks for new version releases periodically by connecting to devstar.cn env_config_keys = Environment Configuration env_config_keys_prompt = The following environment variables will also be applied to your configuration file: config_write_file_prompt = These configuration options will be written into: %s @@ -3163,6 +3165,7 @@ users.allow_git_hook_tooltip = Git Hooks are executed as the OS user running Git users.allow_import_local = May Import Local Repositories users.allow_create_organization = May Create Organizations users.allow_create_devcontainer= May Create Devcontainers +users.allow_create_actrunner= May Create ActRunners users.update_profile = Update User Account users.delete_account = Delete User Account users.cannot_delete_self = "You cannot delete yourself" @@ -3881,7 +3884,7 @@ status.blocked = "Blocked" runners = Runners runners.runner_manage_panel = Runners Management runners.new = Create new Runner -runners.new_notice = How to start a runner +runners.new_notice = How to start a runner manually runners.status = Status runners.id = ID runners.name = Name diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index fb0233ea8d..ceceb7e2c4 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -201,8 +201,8 @@ license_desc = Go get [install] install=Instalación title=Configuración inicial -docker_helper=Si está ejecutando Gitea dentro de un contenedor Docker, por favor lea la documentación antes de realizar cambios en la configuración. -require_db_desc=Gitea requiere una base de datos MySQL, PostgreSQL, MSSQL, SQLite3 o TiDB (usar el protocolo MySQL). +k8s_helper=Si está ejecutando DevStar dentro de un contenedor Kubernetes, por favor lea la documentación antes de realizar cambios en la configuración. +require_db_desc=DevStar requiere una base de datos MySQL, PostgreSQL, MSSQL, SQLite3 o TiDB (usar el protocolo MySQL). db_title=Configuración de base de datos db_type=Tipo de base de datos host=Servidor @@ -306,7 +306,7 @@ password_algorithm=Algoritmo Hash de Contraseña invalid_password_algorithm=Algoritmo hash de contraseña no válido password_algorithm_helper=Establece el algoritmo de hashing de contraseña. Los algoritmos tienen diferentes requisitos y fuerza. El algoritmo argon2 es bastante seguro, pero usa mucha memoria y puede ser inapropiado para sistemas pequeños. enable_update_checker=Activar comprobador de actualizaciones -enable_update_checker_helper=Comprueba el lanzamiento de nuevas versiones periódicamente en gitea.io. +enable_update_checker_helper=Comprueba el lanzamiento de nuevas versiones periódicamente en devstar.cn. env_config_keys=Configuración del entorno env_config_keys_prompt=Las siguientes variables de entorno también se aplicarán a su archivo de configuración: diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index 84a5b60175..955a48dabd 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -134,7 +134,7 @@ license_desc = Go get [install] install=نصب و راه اندازی title=تنظیمات اولیه -docker_helper=اگر گیتی را با داکر اجرا کرده‌اید، لطفا قبل از هر تغییری مستندات را مطالعه نمایید. +k8s_helper=اگر گیتی را با داکر اجرا کرده‌اید، لطفا قبل از هر تغییری مستندات را مطالعه نمایید. db_title=تنظیمات پایگاه داده db_type=نوع پایگاه داده host=میزبان diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index b8e21790d6..20839187fb 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -152,8 +152,8 @@ license_desc = Go get [install] install=Asennus title=Alkuperäiset asetukset -docker_helper=Jos ajat Giteaa Dockerin sisällä, lue ohjeet ennen minkään asetuksen muuttamista. -require_db_desc=Gitea tarvitsee toimiakseen MySQL, PostgreSQL, MSSQL, SQLite3 tai TiDB (MySQL protokolla) tietokannan. +k8s_helper=Jos ajat DevStara Kubernetesin sisällä, lue ohjeet ennen minkään asetuksen muuttamista. +require_db_desc=DevStar tarvitsee toimiakseen MySQL, PostgreSQL, MSSQL, SQLite3 tai TiDB (MySQL protokolla) tietokannan. db_title=Tietokanta asetukset db_type=Tietokanta tyyppi host=Isäntä diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 709a14fc97..a88266264e 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -254,8 +254,8 @@ license_desc = Go get install=Installation installing_desc=Installation en cours, veuillez patienter… title=Configuration initiale -docker_helper=Si vous exécutez Gitea dans Docker, veuillez lire la documentation avant de modifier les paramètres. -require_db_desc=Gitea nécessite MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (avec le protocole MySQL). +k8s_helper=Si vous exécutez DevStar dans Kubernetes, veuillez lire la documentation avant de modifier les paramètres. +require_db_desc=DevStar nécessite MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (avec le protocole MySQL). db_title=Paramètres de la base de données db_type=Type de base de données host=Hôte @@ -360,7 +360,7 @@ password_algorithm=Algorithme de hachage du mot de passe invalid_password_algorithm=Algorithme de hachage du mot de passe invalide password_algorithm_helper=Définissez l’algorithme de hachage du mot de passe. Les algorithmes ont des exigences matérielles et une résistance différentes. L’algorithme argon2 est bien sécurisé mais utilise beaucoup de mémoire et peut être inapproprié pour les systèmes limités en ressources. enable_update_checker=Activer la vérification des mises-à-jour -enable_update_checker_helper=Vérifie les mises à jour régulièrement en se connectant à gitea.io. +enable_update_checker_helper=Vérifie les mises à jour régulièrement en se connectant à devstar.cn. env_config_keys=Configuration de l'environnement env_config_keys_prompt=Les variables d'environnement suivantes seront également ajoutées à votre fichier de configuration : config_write_file_prompt=Ces options de configuration seront écrites dans : %s diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index a8024b95c1..45a5d36ad0 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -254,7 +254,7 @@ license_desc = Go get install=Suiteáil installing_desc=Suiteáil anois, fan go fóill... title=Cumraíocht Tosaigh -docker_helper=Má ritheann tú Gitea taobh istigh de Docker, léigh an doiciméadúchán roimh aon socruithe a athrú. +k8s_helper=Má ritheann tú DevStar taobh istigh de Kubernetes, léigh an doiciméadúchán roimh aon socruithe a athrú. require_db_desc=Éilíonn Gitea MySQL, PostgreSQL, MSSQL, SQLite3 nó TiDB (prótacal MySQL). db_title=Socruithe Bunachar Sonraí db_type=Cineál Bunachar Sonraí @@ -360,7 +360,7 @@ password_algorithm=Algartam Hais Pasfhocal invalid_password_algorithm=Algartam hais pasfhocail neamhbhailí password_algorithm_helper=Socraigh an algartam hashing pasfhocal. Tá riachtanais agus neart éagsúla ag halgartaim. Tá an algartam argon2 sách slán ach úsáideann sé go leor cuimhne agus d'fhéadfadh sé a bheith míchuí do chórais bheaga. enable_update_checker=Cumasaigh Seiceoir Nuashonraithe -enable_update_checker_helper=Seiceálacha ar eisiúintí leagan nua go tréimhsiúil trí nascadh le gitea.io. +enable_update_checker_helper=Seiceálacha ar eisiúintí leagan nua go tréimhsiúil trí nascadh le devstar.cn. env_config_keys=Cumraíocht Comhshaoil env_config_keys_prompt=Cuirfear na hathróga comhshaoil seo a leanas i bhfeidhm ar do chomhad cumraíochta freisin: config_write_file_prompt=Scríobhfar na roghanna cumraíochta seo isteach: %s diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index ea25c6920c..8dd0bbf71b 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -123,7 +123,7 @@ license_desc = Go get [install] install=Telepítés title=Kezdeti konfiguráció -docker_helper=Ha ön a Gitea-t Docker-ből futtatja, kérem olvassa el a dokumentációt a beállítások megváltoztatása előtt. +k8s_helper=Ha ön a DevStar-t Kubernetes-ből futtatja, kérem olvassa el a dokumentációt a beállítások megváltoztatása előtt. db_title=Adatbázis beállítások db_type=Adatbázis típusa host=Kiszolgáló diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index ee76317f31..4fc38c6699 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -148,8 +148,8 @@ license_desc = Go get [install] install=Uppsetning title=Upphafleg Uppsetning -docker_helper=Ef þú keyrir Gitea inni í Docker þá viltu vinsamlegast lesa leiðbeiningaritið áður en þú breytir stillingum. -require_db_desc=Gitea krefst MySQL, PostgreSQL, MSSQL, SQLite3 eða TiDB (MySQL samskiptareglur). +k8s_helper=Ef þú keyrir DevStar inni í Kubernetes þá viltu vinsamlegast lesa leiðbeiningaritið áður en þú breytir stillingum. +require_db_desc=DevStar krefst MySQL, PostgreSQL, MSSQL, SQLite3 eða TiDB (MySQL samskiptareglur). db_title=Gagnagrunnsstillingar db_type=Tegund Gagnagrunns host=Hýsill diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 5a6860b7d5..3c3b210ddd 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -154,8 +154,8 @@ license_desc = Go get [install] install=Installazione title=Configurazione Iniziale -docker_helper=Se stai usando Gitea con Docker, leggi la documentazione prima di cambiare qualsiasi impostazione. -require_db_desc=Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). +k8s_helper=Se stai usando DevStar con Kubernetes, leggi la documentazione prima di cambiare qualsiasi impostazione. +require_db_desc=DevStar requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). db_title=Impostazioni Database db_type=Tipo di database host=Host diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index c9db790c1f..f206f32733 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -251,8 +251,8 @@ license_desc = Go get install=インストール installing_desc=インストール中です、お待ちください... title=初期設定 -docker_helper=GiteaをDocker内で実行する場合は、設定を変更する前にドキュメントを読んでください。 -require_db_desc=Giteaには、MySQL、PostgreSQL、MSSQL、SQLite3、またはTiDB(MySQL プロトコル) が必要です。 +k8s_helper=DevStarをKubernetes内で実行する場合は、設定を変更する前にドキュメントを読んでください。 +require_db_desc=DevStarには、MySQL、PostgreSQL、MSSQL、SQLite3、またはTiDB(MySQL プロトコル) が必要です。 db_title=データベース設定 db_type=データベースのタイプ host=ホスト @@ -357,7 +357,7 @@ password_algorithm=パスワードハッシュアルゴリズム invalid_password_algorithm=無効なパスワードハッシュアルゴリズム password_algorithm_helper=パスワードハッシュアルゴリズムを設定します。 アルゴリズムにより動作要件と強度が異なります。 argon2アルゴリズムはかなり安全ですが、多くのメモリを使用するため小さなシステムには適さない場合があります。 enable_update_checker=アップデートチェッカーを有効にする -enable_update_checker_helper=gitea.ioに接続して定期的に新しいバージョンのリリースを確認します。 +enable_update_checker_helper=devstar.cnに接続して定期的に新しいバージョンのリリースを確認します。 env_config_keys=環境設定 env_config_keys_prompt=以下の環境変数も設定ファイルに適用されます: config_write_file_prompt=これらの設定オプションは %s に書き込まれます diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index 4d53bbf2d7..f97ef21b53 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -115,7 +115,7 @@ license_desc = Go get [install] install=설치 title=초기 설정 -docker_helper=Gitea를 Docker에서 실행하려면 설정 전에 이 문서를 읽어보세요. +k8s_helper=DevStar를 Kubernetes에서 실행하려면 설정 전에 이 문서를 읽어보세요. db_title=데이터베이스 설정 db_type=데이터베이스 유형 host=호스트 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index e9c9d96d54..e23fac4924 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -206,8 +206,8 @@ license_desc = Go get [install] install=Instalācija title=Sākotnējā konfigurācija -docker_helper=Ja Gitea ir uzstādīts Docker konteinerī, izlasiet vadlīninas pirms maināt iestatījumus. -require_db_desc=Gitea nepieciešams MySQL, PostgreSQL, MSSQL, SQLite3 vai TiDB (izmantojot MySQL protokolu). +k8s_helper=Ja DevStar ir uzstādīts Kubernetes konteinerī, izlasiet vadlīninas pirms maināt iestatījumus. +require_db_desc=DevStar nepieciešams MySQL, PostgreSQL, MSSQL, SQLite3 vai TiDB (izmantojot MySQL protokolu). db_title=Datu bāzes iestatījumi db_type=Datu bāzes veids host=Resursdators @@ -311,7 +311,7 @@ password_algorithm=Paroles jaucējsummas algoritms invalid_password_algorithm=Kļūdaina paroles jaucējfunkcija password_algorithm_helper=Norādiet paroles jaucējalgoritmu. Algoritmi atšķirās pēc prasībām pret resursiem un stipruma. Argon2 algoritms ir drošs, bet tam nepieciešams daudz operatīvās atmiņas, līdz ar ko tas var nebūt piemērots sistēmām ar maz pieejamajiem resursiem. enable_update_checker=Iespējot jaunu versiju paziņojumus -enable_update_checker_helper=Periodiski pārbaudīt jaunu version pieejamību, izgūstot datus no gitea.io. +enable_update_checker_helper=Periodiski pārbaudīt jaunu version pieejamību, izgūstot datus no devstar.cn. env_config_keys=Vides konfigurācija env_config_keys_prompt=Šie vides mainīgie tiks pielietoti arī konfigurācijas failā: diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index d82de9aedc..d41ea12cb7 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -153,8 +153,8 @@ license_desc = Go get [install] install=Installatie title=Initiële configuratie -docker_helper=Als je gitea draait in Docker, Lees eerst de documentatie voordat je een instelling aanpast. -require_db_desc=Gitea vereist MySQL, PostgreSQL, MSSQL, SQLite3 of TiDB (MySQL protocol). +k8s_helper=Als je gitea draait in Kubernetes, Lees eerst de documentatie voordat je een instelling aanpast. +require_db_desc=DevStar vereist MySQL, PostgreSQL, MSSQL, SQLite3 of TiDB (MySQL protocol). db_title=Database-instellingen db_type=Database-type host=Server diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 16760db5b1..0c3c2c10f7 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -149,8 +149,8 @@ license_desc = Go get [install] install=Instalacja title=Wstępna konfiguracja -docker_helper=Jeśli używasz Gitea za pomocą Docker'a, przeczytaj dokumentację przed wprowadzeniem jakichkolwiek zmian. -require_db_desc=Gitea wymaga MySQL, PostgreSQL, MSSQL, SQLite3 lub TiDB (protokół MySQL). +k8s_helper=Jeśli używasz DevStar za pomocą Kubernetes'a, przeczytaj dokumentację przed wprowadzeniem jakichkolwiek zmian. +require_db_desc=DevStar wymaga MySQL, PostgreSQL, MSSQL, SQLite3 lub TiDB (protokół MySQL). db_title=Ustawienia bazy danych db_type=Typ bazy danych host=Serwer diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index c06c214846..846fe93932 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -202,8 +202,8 @@ license_desc = Go get [install] install=Instalação title=Configuração inicial -docker_helper=Se você está rodando o Gitea dentro do Docker, por favor leia a documentação cuidadosamente antes de alterar qualquer coisa nesta página. -require_db_desc=Gitea requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL). +k8s_helper=Se você está rodando o DevStar dentro do Kubernetes, por favor leia a documentação cuidadosamente antes de alterar qualquer coisa nesta página. +require_db_desc=DevStar requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL). db_title=Configurações de banco de dados db_type=Tipo de banco de dados host=Servidor @@ -307,7 +307,7 @@ password_algorithm=Algoritmo Hash de Senha invalid_password_algorithm=Algoritmo de hash de senha inválido password_algorithm_helper=Escolha o algoritmo de hash para as senhas. Diferentes algoritmos têm requerimentos e forças diversos. O algoritmo argon2 é bastante seguro, mas usa muita memória e pode ser inapropriado para sistemas com menos recursos. enable_update_checker=Habilitar Verificador de Atualizações -enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao gitea.io. +enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao devstar.cn. env_config_keys=Configuração do ambiente env_config_keys_prompt=As seguintes variáveis de ambiente também serão aplicadas ao seu arquivo de configuração: diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index f0f769ddfc..6096500f66 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -254,8 +254,8 @@ license_desc = Go get install=Instalação installing_desc=Instalando agora, por favor aguarde... title=Configuração inicial -docker_helper=Se correr o Gitea dentro do Docker, leia a documentação antes de alterar quaisquer configurações. -require_db_desc=Gitea requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL). +k8s_helper=Se correr o DevStar dentro do Kubernetes, leia a documentação antes de alterar quaisquer configurações. +require_db_desc=DevStar requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL). db_title=Configurações da base de dados db_type=Tipo de base de dados host=Servidor @@ -360,7 +360,7 @@ password_algorithm=Algoritmo de Hash da Senha invalid_password_algorithm=Algoritmo de hash da senha inválido password_algorithm_helper=Definir o algoritmo de hash da senha. Os algoritmos têm requisitos e resistência distintos. `argon2` é bastante seguro, mas usa muita memória e pode ser inapropriado para sistemas pequenos. enable_update_checker=Habilitar verificador de novidades -enable_update_checker_helper=Verifica, periodicamente, se foi lançada alguma versão nova, fazendo uma ligação ao gitea.io. +enable_update_checker_helper=Verifica, periodicamente, se foi lançada alguma versão nova, fazendo uma ligação ao devstar.cn. env_config_keys=Configuração do ambiente env_config_keys_prompt=As seguintes variáveis de ambiente também serão aplicadas ao seu ficheiro de configuração: config_write_file_prompt=Estas opções de configuração serão escritas em: %s diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 2f9812bf19..ba31cad94d 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -201,8 +201,8 @@ license_desc = Go get [install] install=Установка title=Начальная конфигурация -docker_helper=Если вы запускаете Gitea внутри Docker, пожалуйста внимательно прочтите документацию перед тем, как изменить любые настройки. -require_db_desc=Gitea требует MySQL, PostgreSQL, MSSQL, SQLite3 или TiDB (через протокол MySQL). +k8s_helper=Если вы запускаете DevStar внутри Kubernetes, пожалуйста внимательно прочтите документацию перед тем, как изменить любые настройки. +require_db_desc=DevStar требует MySQL, PostgreSQL, MSSQL, SQLite3 или TiDB (через протокол MySQL). db_title=Настройки базы данных db_type=Тип базы данных host=Хост @@ -306,7 +306,7 @@ password_algorithm=Алгоритм хеширования пароля invalid_password_algorithm=Некорректный алгоритм хеширования пароля password_algorithm_helper=Задайте алгоритм хеширования паролей. Алгоритмы имеют различные требования и стойкость. Алгоритм argon2 довольно безопасен, но он использует много памяти и может не подходить для слабых систем. enable_update_checker=Включить проверку обновлений -enable_update_checker_helper=Периодически проверяет наличие новых версий, подключаясь к gitea.io. +enable_update_checker_helper=Периодически проверяет наличие новых версий, подключаясь к devstar.cn. env_config_keys=Настройка окружения env_config_keys_prompt=Следующие переменные окружения также будут применены к вашему конфигурационному файлу: diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 43a5837d1c..5f93060206 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -134,7 +134,7 @@ license_desc = Go get [install] install=ස්ථාපනය title=මූලික වින්යාසය -docker_helper=ඔබ Docker තුළ Gitea ධාවනය කරන්නේ නම්, කරුණාකර ඕනෑම සැකසුම් වෙනස් කිරීමට පෙර ලියකියවිලි කියවන්න. +k8s_helper=ඔබ Kubernetes තුළ DevStar ධාවනය කරන්නේ නම්, කරුණාකර ඕනෑම සැකසුම් වෙනස් කිරීමට පෙර ලියකියවිලි කියවන්න. db_title=දත්ත සමුදායේ සැකසුම් db_type=දත්ත සමුදායේ වර්ගය host=සත්කාරක diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index 07ee784316..cf3f13a752 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -200,8 +200,8 @@ license_desc = Go get [install] install=Inštalácia title=Východzia konfigurácia -docker_helper=Ak spúšťate Gitea v Docker kontajneri, prečítajte si dokumentáciu pred zmenou akýchkoľvek nastavení. -require_db_desc=Gitea vyžaduje MySQL, PostgreSQL, MSSQL, SQLite3 alebo TiDB (MySQL protokol). +k8s_helper=Ak spúšťate DevStar v Kubernetes kontajneri, prečítajte si dokumentáciu pred zmenou akýchkoľvek nastavení. +require_db_desc=DevStar vyžaduje MySQL, PostgreSQL, MSSQL, SQLite3 alebo TiDB (MySQL protokol). db_title=Nastavenie databázy db_type=Typ databázy host=Host @@ -303,7 +303,7 @@ password_algorithm=Hašovací algoritmus hesla invalid_password_algorithm=Neplatný hash algoritmus hesla password_algorithm_helper=Nastavte algoritmus hashovania hesla. Algoritmy majú rôzne požiadavky a silu. Algoritmus argon2 je pomerne bezpečný, ale využíva veľa pamäte a môže byť nevhodný pre malé systémy. enable_update_checker=Povoliť kontrolu aktualizácií -enable_update_checker_helper=Pravidelne kontroluje nové verzie pripojením k gitea.io. +enable_update_checker_helper=Pravidelne kontroluje nové verzie pripojením k devstar.cn. [home] uname_holder=Používateľské meno alebo emailová adresa diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 899c06c142..027b3ac853 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -124,7 +124,7 @@ license_desc = Go get [install] install=Installation title=Ursprunglig konfiguration -docker_helper=Om du kör Gitea i Docker, vänligen läs igenom dokumentationen innan några inställningar ändras. +k8s_helper=Om du kör DevStar i Kubernetes, vänligen läs igenom dokumentationen innan några inställningar ändras. db_title=Databasinställningar db_type=Databastyp host=Server diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index ab1ea3b33f..4d7c89da87 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -251,8 +251,8 @@ license_desc = Go get install=Kurulum installing_desc=Şimdi kuruluyor, lütfen bekleyin... title=Başlangıç Yapılandırması -docker_helper=Eğer Gitea'yı Docker içerisinde çalıştırıyorsanız, lütfen herhangi bir değişiklik yapmadan önce belgeleri okuyun. -require_db_desc=Gitea MySQL, PostgreSQL, MSSQL, SQLite3 veya TiDB (MySQL protokolü) gerektirir. +k8s_helper=Eğer DevStar'yı Kubernetes içerisinde çalıştırıyorsanız, lütfen herhangi bir değişiklik yapmadan önce belgeleri okuyun. +require_db_desc=DevStar MySQL, PostgreSQL, MSSQL, SQLite3 veya TiDB (MySQL protokolü) gerektirir. db_title=Veritabanı Ayarları db_type=Veritabanı Türü host=Sunucu @@ -357,7 +357,7 @@ password_algorithm=Parola Hash Algoritması invalid_password_algorithm=Hatalı parola hash algoritması password_algorithm_helper=Parola hash algoritmasını ayarlayın. Algoritmalar değişen gereksinimlere ve güce sahiptirler. argon2 algoritması iyi özelliklere sahip olmasına rağmen fazla miktarda bellek kullanır ve küçük sistemler için uygun olmayabilir. enable_update_checker=Güncelleme Denetleyicisini Etkinleştir -enable_update_checker_helper=Düzenli olarak gitea.io'ya bağlanarak yeni yayınlanan sürümleri denetler. +enable_update_checker_helper=Düzenli olarak devstar.cn'ya bağlanarak yeni yayınlanan sürümleri denetler. env_config_keys=Ortam Yapılandırma env_config_keys_prompt=Aşağıdaki ortam değişkenleri de yapılandırma dosyanıza eklenecektir: config_write_file_prompt=Bu yapılandırma seçenekleri şuraya yazılacak: %s diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index cd4586c5a7..c1aad9c606 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -246,8 +246,8 @@ license_desc = Go get install=Встановлення installing_desc=Встановлення, будь ласка, зачекайте... title=Початкова конфігурація -docker_helper=Якщо ви запускаєте Gitea у Docker, будь ласка, прочитайте документацію перед тим, як змінювати будь-які налаштування. -require_db_desc=Gitea потребує MySQL, PostgreSQL, MSSQL, SQLite3 або TiDB (протокол MySQL). +k8s_helper=Якщо ви запускаєте DevStar у Kubernetes, будь ласка, прочитайте документацію перед тим, як змінювати будь-які налаштування. +require_db_desc=DevStar потребує MySQL, PostgreSQL, MSSQL, SQLite3 або TiDB (протокол MySQL). db_title=Налаштування бази даних db_type=Тип бази даних host=Хост @@ -352,7 +352,7 @@ password_algorithm=Алгоритм хешування пароля invalid_password_algorithm=Недійсний хеш-алгоритм пароля password_algorithm_helper=Встановіть алгоритм хешування пароля. Алгоритми мають різні вимоги та стійкість. Алгоритм argon2 є досить безпечним, але використовує багато пам'яті і може бути недоречним для малих систем. enable_update_checker=Увімкнути перевірку оновлень -enable_update_checker_helper=Періодично перевіряти наявність нових версій, підключаючись до gitea.io. +enable_update_checker_helper=Періодично перевіряти наявність нових версій, підключаючись до devstar.cn. env_config_keys=Конфігурація середовища env_config_keys_prompt=Наступні змінні середовища також будуть застосовані до вашого файлу конфігурації: config_write_file_prompt=Ці параметри будуть записані в: %s diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index f020fc6467..d99c19dfef 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -251,8 +251,8 @@ license_desc=所有的代码都开源在 官方文档 后再对本页面进行填写。 -require_db_desc=Gitea 需要使用 MySQL、PostgreSQL、MSSQL、SQLite3 或 TiDB (MySQL协议) 等数据库 +k8s_helper=如果您正在使用 Kubernetes 运行 DevStar,请务必先仔细阅读 官方文档 后再对本页面进行填写。 +require_db_desc=DevStar 需要使用 MySQL、PostgreSQL、MSSQL、SQLite3 或 TiDB (MySQL协议) 等数据库 db_title=数据库设置 db_type=数据库类型 host=数据库主机 @@ -358,8 +358,10 @@ default_keep_email_private=默认情况下隐藏邮箱地址 default_keep_email_private_popup=默认情况下,隐藏新用户帐户的邮箱地址。 default_allow_create_organization=默认情况下允许创建组织 default_allow_create_devcontainer=默认情况下允许创建容器 +default_allow_create_actrunner=默认情况下允许创建工作流运行器 default_allow_create_organization_popup=默认情况下, 允许新用户帐户创建组织。 default_allow_create_devcontainer_popup=默认情况下, 允许新用户帐户创建容器。 +default_allow_create_actrunner_popup=默认情况下, 允许新用户帐户创建工作流运行器。 default_enable_timetracking=默认情况下启用时间跟踪 default_enable_timetracking_popup=默认情况下启用新仓库的时间跟踪。 no_reply_address=隐藏邮件域 @@ -368,7 +370,7 @@ password_algorithm=密码哈希算法 invalid_password_algorithm=无效的密码哈希算法 password_algorithm_helper=设置密码散列算法。算法有不同的要求和强度。 argon2 算法相当安全,但使用大量内存,因此可能不适合小型系统。 enable_update_checker=启用更新检查 -enable_update_checker_helper=通过连接到 gitea.io 定期检查新版本发布。 +enable_update_checker_helper=通过连接到 devstar.cn 定期检查新版本发布。 env_config_keys=环境配置 env_config_keys_prompt=以下环境变量也将应用于您的配置文件: config_write_file_prompt=这些配置选项将写入以下位置: %s @@ -3153,6 +3155,7 @@ users.allow_git_hook_tooltip=Git 钩子将会以操作系统用户运行,拥 users.allow_import_local=允许导入本地仓库 users.allow_create_organization=允许创建组织 users.allow_create_devcontainer=允许创建开发容器 +users.allow_create_actrunner=允许创建工作流运行器 users.update_profile=更新帐户 users.delete_account=删除帐户 users.cannot_delete_self=您不能删除自己 @@ -3412,6 +3415,7 @@ config.reset_password_code_lives=恢复账户验证码过期时间 config.default_keep_email_private=默认隐藏邮箱地址 config.default_allow_create_organization=默认情况下允许创建组织 config.default_allow_create_devcontainer=默认情况下允许创建 DevContainer +config.default_allow_create_actrunner=默认情况下允许创建 ActRunner config.enable_timetracking=启用时间跟踪 config.default_enable_timetracking=默认情况下启用时间跟踪 config.default_allow_only_contributors_to_track_time=仅允许成员跟踪时间 @@ -3869,7 +3873,7 @@ status.blocked=阻塞中 runners=运行器 runners.runner_manage_panel=运行器管理 runners.new=创建新运行器 -runners.new_notice=如何启动一个运行器 +runners.new_notice=如何手动启动一个运行器 runners.status=状态 runners.id=ID runners.name=名称 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 28af9facc4..a83e2eb932 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -243,8 +243,8 @@ license_desc=所有的代码都开源在 安裝指南再來調整設定。 -require_db_desc=Gitea 需要 MySQL、PostgreSQL、SQLite3、MSSQL、TiDB (MySQL 協定) 等其中一項。 +k8s_helper=如果您在 Kubernetes 中執行 DevStar,請先閱讀安裝指南再來調整設定。 +require_db_desc=DevStar 需要 MySQL、PostgreSQL、SQLite3、MSSQL、TiDB (MySQL 協定) 等其中一項。 db_title=資料庫設定 db_type=資料庫類型 host=主機 @@ -349,7 +349,7 @@ password_algorithm=密碼雜湊演算法 invalid_password_algorithm=無效的密碼雜湊演算法 password_algorithm_helper=設定密碼雜湊演算法。演算法有不同的需求與強度。argon2 演算法雖然較安全但會使用大量記憶體,可能不適用於小型系統。 enable_update_checker=啟用更新檢查器 -enable_update_checker_helper=定期連線到 gitea.io 檢查更新。 +enable_update_checker_helper=定期連線到 devstar.cn 檢查更新。 env_config_keys=環境組態設定 env_config_keys_prompt=下列環境變數也會套用到您的組態檔: config_write_file_prompt=這些配置選項將被寫入到: %s diff --git a/public/assets/install.sh b/public/assets/install.sh index ae0fe411e9..5ac4155f4e 100755 --- a/public/assets/install.sh +++ b/public/assets/install.sh @@ -93,6 +93,13 @@ function install { sudo docker pull devstar.cn/devstar/webterminal:latest success "Successfully pulled devstar.cn/devstar/webterminal:latest" fi + if sudo docker pull mengning997/act_runner:latest; then + sudo docker tag mengning997/act_runner:latest devstar.cn/devstar/act_runner:latest + success "Successfully pulled mengning997/act_runner:latest renamed to devstar.cn/devstar/act_runner:latest" + else + sudo docker pull devstar.cn/devstar/act_runner:latest + success "Successfully pulled devstar.cn/devstar/act_runner:latest" + fi } # Function to start @@ -140,7 +147,10 @@ function stop { fi if [ $(docker ps -a --filter "name=^/webterminal-" -q | wc -l) -gt 0 ]; then sudo docker stop $(docker ps -a --filter "name=^/webterminal-" -q) && sudo docker rm -f $(docker ps -a --filter "name=^/webterminal-" -q) - fi + fi + if [ $(docker ps -a --filter "name=^/runner-" -q | wc -l) -gt 0 ]; then + sudo docker stop $(docker ps -a --filter "name=^/runner-" -q) && sudo docker rm -f $(docker ps -a --filter "name=^/runner-" -q) + fi } # Function to logs diff --git a/public/assets/version.json b/public/assets/version.json new file mode 100644 index 0000000000..fc240f0b03 --- /dev/null +++ b/public/assets/version.json @@ -0,0 +1 @@ +{"latest":{"version":"1.25.1"}} \ No newline at end of file diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index c14a24ef02..34d832ec16 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -246,6 +246,7 @@ func EditUser(ctx *context.APIContext) { MaxRepoCreation: optional.FromPtr(form.MaxRepoCreation), AllowCreateOrganization: optional.FromPtr(form.AllowCreateOrganization), AllowCreateDevcontainer: optional.FromPtr(form.AllowCreateDevcontainer), + AllowCreateActRunner: optional.FromPtr(form.AllowCreateActRunner), IsRestricted: optional.FromPtr(form.Restricted), } diff --git a/routers/install/install.go b/routers/install/install.go index 579238b1af..0ed16ce5aa 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -156,6 +156,7 @@ func Install(ctx *context.Context) { form.DefaultKeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate form.DefaultAllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization form.DefaultAllowCreateDevcontainer = setting.Service.DefaultAllowCreateDevcontainer + form.DefaultAllowCreateActRunner = setting.Service.DefaultAllowCreateActRunner form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking form.NoReplyAddress = setting.Service.NoReplyAddress form.PasswordAlgorithm = hash.ConfigHashAlgorithm(setting.PasswordHashAlgo) @@ -492,6 +493,7 @@ func SubmitInstall(ctx *context.Context) { cfg.Section("service").Key("DEFAULT_KEEP_EMAIL_PRIVATE").SetValue(strconv.FormatBool(form.DefaultKeepEmailPrivate)) cfg.Section("service").Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").SetValue(strconv.FormatBool(form.DefaultAllowCreateOrganization)) cfg.Section("service").Key("DEFAULT_ALLOW_CREATE_DEVCONTAINER").SetValue(strconv.FormatBool(form.DefaultAllowCreateDevcontainer)) + cfg.Section("service").Key("DEFAULT_ALLOW_CREATE_ACTRUNNER").SetValue(strconv.FormatBool(form.DefaultAllowCreateActRunner)) cfg.Section("service").Key("DEFAULT_ENABLE_TIMETRACKING").SetValue(strconv.FormatBool(form.DefaultEnableTimetracking)) cfg.Section("service").Key("NO_REPLY_ADDRESS").SetValue(form.NoReplyAddress) cfg.Section("cron.update_checker").Key("ENABLED").SetValue(strconv.FormatBool(form.EnableUpdateChecker)) @@ -630,17 +632,24 @@ func SubmitInstall(ctx *context.Context) { if form.K8sEnable { //K8s环境检测 + cfg.Section("devstar.devcontainer").Key("HOST").SetValue(form.Domain) } else { if !checkDocker(ctx, &form) { ctx.RenderWithErr("There is no docker environment", tplInstall, &form) return } } + // 注册Global Runners + if setting.Runner.AutoStart { + for i := 0; i < setting.Runner.Count; i++ { + runners_service.RegistGlobalRunner(ctx) + } + } setting.ClearEnvConfigKeys() log.Info("First-time run install finished!") InstallDone(ctx) - + go func() { // Sleep for a while to make sure the user's browser has loaded the post-install page and its assets (images, css, js) // What if this duration is not long enough? That's impossible -- if the user can't load the simple page in time, how could they install or use Gitea in the future .... @@ -654,8 +663,6 @@ func SubmitInstall(ctx *context.Context) { return } } - runners_service.RegistGlobalRunner(otherCtx) - // Now get the http.Server from this request and shut it down // NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown srv := ctx.Value(http.ServerContextKey).(*http.Server) diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 5905d37755..11598411d4 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -437,6 +437,7 @@ func EditUserPost(ctx *context.Context) { MaxRepoCreation: optional.Some(form.MaxRepoCreation), AllowCreateOrganization: optional.Some(form.AllowCreateOrganization), AllowCreateDevcontainer: optional.Some(form.AllowCreateDevcontainer), + AllowCreateActRunner: optional.Some(form.AllowCreateActRunner), IsRestricted: optional.Some(form.Restricted), Visibility: optional.Some(form.Visibility), Language: optional.Some(form.Language), @@ -450,7 +451,6 @@ func EditUserPost(ctx *context.Context) { } return } - log.Trace("Account profile updated by admin (%s): %s", ctx.Doer.Name, u.Name) if form.Reset2FA { tf, err := auth.GetTwoFactorByUID(ctx, u.ID) diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go index aaf06a84b0..c00bba4a9e 100644 --- a/routers/web/shared/actions/runners.go +++ b/routers/web/shared/actions/runners.go @@ -160,6 +160,7 @@ func Runners(ctx *context.Context) { ctx.Data["RunnerOwnerID"] = opts.OwnerID ctx.Data["RunnerRepoID"] = opts.RepoID ctx.Data["SortType"] = opts.Sort + ctx.Data["AllowCreateActRunner"] = ctx.Doer.AllowCreateActRunner pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) @@ -300,6 +301,14 @@ func RegisterARunner(ctx *context.Context) { ctx.ServerError("getRunnersCtx", err) return } + + // 检查用户是否有权创建 runner + if !ctx.Doer.AllowCreateActRunner { + ctx.Flash.Error(ctx.Tr("actions.runners.create_runner_permission_denied")) + ctx.Redirect(rCtx.RedirectLink) + return + } + token, err := actions_model.NewRunnerToken(ctx, rCtx.OwnerID, rCtx.RepoID) if err != nil { ctx.ServerError("NewRunnerToken", err) diff --git a/services/context/repo.go b/services/context/repo.go index fbe8178bd6..cabdd1d158 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -399,6 +399,8 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { ctx.Data["Permission"] = &ctx.Repo.Permission if ctx.Doer != nil { ctx.Data["AllowCreateDevcontainer"] = ctx.Doer.AllowCreateDevcontainer + ctx.Data["AllowCreateActRunner"] = ctx.Doer.AllowCreateActRunner + } else { query := ctx.Req.URL.Query() userID := query.Get("user") @@ -416,6 +418,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { return } ctx.Data["AllowCreateDevcontainer"] = u.AllowCreateDevcontainer + ctx.Data["AllowCreateActRunner"] = u.AllowCreateActRunner } if repo.IsMirror { diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 0018c5facc..25015a997f 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -150,7 +150,7 @@ func registerUpdateGiteaChecker() { RunAtStart: false, Schedule: "@every 168h", }, - HTTPEndpoint: "https://dl.gitea.com/gitea/version.json", + HTTPEndpoint: "https://devstar.cn/assets/version.json", }, func(ctx context.Context, _ *user_model.User, config Config) error { updateCheckerConfig := config.(*UpdateCheckerConfig) return updatechecker.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint) diff --git a/services/devcontainer/devcontainer.go b/services/devcontainer/devcontainer.go index 8f521e1789..5b6aa6570e 100644 --- a/services/devcontainer/devcontainer.go +++ b/services/devcontainer/devcontainer.go @@ -161,7 +161,29 @@ func GetWebTerminalURL(ctx context.Context, userID, repoID int64) (string, error return "", err } if cfg.Section("k8s").Key("ENABLE").Value() == "true" { - //k8s的逻辑 + // K8s 模式:使用 Istio Gateway + VirtualService + log.Info("GetWebTerminalURL: 使用 Istio 模式获取 WebTerminal URL for DevContainer: %s", devcontainerName) + + // 从配置中读取域名 + domain := cfg.Section("server").Key("DOMAIN").Value() + + // 从容器名称中提取用户名和仓库名 + parts := strings.Split(devcontainerName, "-") + var username, repoName string + if len(parts) >= 2 { + username = parts[0] + repoName = parts[1] + } else { + username = "unknown" + repoName = "unknown" + } + + // 构建基于 Istio Gateway 的 URL + path := fmt.Sprintf("/%s/%s/dev-container-webterminal", username, repoName) + webTerminalURL := fmt.Sprintf("http://%s%s", domain, path) + + log.Info("GetWebTerminalURL: 生成 Istio WebTerminal URL: %s", webTerminalURL) + return webTerminalURL, nil } return "", nil } @@ -181,6 +203,7 @@ func GetWebTerminalURL(ctx context.Context, userID, repoID int64) (string, error 10已删除 */ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string, error) { + log.Info("GetDevContainerStatus: Starting - userID: %s, repoID: %s", userID, repoID) var id int var containerName string @@ -188,6 +211,7 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string, var realTimeStatus uint16 cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf) if err != nil { + log.Error("GetDevContainerStatus: Failed to load config: %v", err) return "", err } dbEngine := db.GetEngine(ctx) @@ -197,19 +221,42 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string, Where("user_id = ? AND repo_id = ?", userID, repoID). Get(&status, &id, &containerName) if err != nil { + log.Error("GetDevContainerStatus: Failed to query database: %v", err) return "", err } + log.Info("GetDevContainerStatus: Database query result - id: %d, containerName: %s, status: %d", id, containerName, status) + if id == 0 { + log.Info("GetDevContainerStatus: No devcontainer found, returning -1") return fmt.Sprintf("%d", -1), nil } realTimeStatus = status + log.Info("GetDevContainerStatus: Initial realTimeStatus: %d", realTimeStatus) switch status { //正在重启 case 6: if cfg.Section("k8s").Key("ENABLE").Value() == "true" { - //k8s的逻辑 + // k8s 逻辑:检查 Pod 是否已恢复运行 + log.Info("GetDevContainerStatus: K8s branch for case 6 (restarting), container: %s", containerName) + opts := &OpenDevcontainerAppDispatcherOptions{ + Name: containerName, + Wait: false, + } + log.Info("GetDevContainerStatus: Calling AssignDevcontainerGetting2K8sOperator with opts: %+v", opts) + devcontainerApp, err := AssignDevcontainerGetting2K8sOperator(&ctx, opts) + if err != nil { + log.Error("GetDevContainerStatus: AssignDevcontainerGetting2K8sOperator failed: %v", err) + } else if devcontainerApp != nil { + log.Info("GetDevContainerStatus: DevcontainerApp retrieved - Name: %s, Ready: %v", devcontainerApp.Name, devcontainerApp.Status.Ready) + if devcontainerApp.Status.Ready { + realTimeStatus = 4 // 已恢复运行 + log.Info("GetDevContainerStatus: Container %s is ready, updating status to 4", containerName) + } + } else { + log.Warn("GetDevContainerStatus: DevcontainerApp is nil for container: %s", containerName) + } } else { containerRealTimeStatus, err := GetDevContainerStatusFromDocker(ctx, containerName) if err != nil { @@ -222,7 +269,24 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string, //正在关闭 case 7: if cfg.Section("k8s").Key("ENABLE").Value() == "true" { - //k8s的逻辑 + // k8s 逻辑:检查 Pod 是否已停止 + log.Info("GetDevContainerStatus: K8s branch for case 7 (stopping), container: %s", containerName) + opts := &OpenDevcontainerAppDispatcherOptions{ + Name: containerName, + Wait: false, + } + log.Info("GetDevContainerStatus: Calling AssignDevcontainerGetting2K8sOperator for stop check with opts: %+v", opts) + devcontainerApp, err := AssignDevcontainerGetting2K8sOperator(&ctx, opts) + if err != nil { + log.Info("GetDevContainerStatus: DevcontainerApp not found or error, considering stopped: %v", err) + realTimeStatus = 8 // 已停止 + } else if devcontainerApp == nil || !devcontainerApp.Status.Ready { + log.Info("GetDevContainerStatus: DevcontainerApp is nil or not ready, considering stopped") + realTimeStatus = 8 // 已停止 + } else { + log.Info("GetDevContainerStatus: DevcontainerApp still running - Name: %s, Ready: %v", devcontainerApp.Name, devcontainerApp.Status.Ready) + } + // 已在外部通过 StopDevContainer 触发,此处仅检查状态 } else { containerRealTimeStatus, err := GetDevContainerStatusFromDocker(ctx, containerName) if err != nil { @@ -240,7 +304,21 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string, break case 9: if cfg.Section("k8s").Key("ENABLE").Value() == "true" { - //k8s的逻辑 + // k8s 逻辑:检查 Pod 是否已删除 + log.Info("GetDevContainerStatus: K8s branch for case 9 (deleting), container: %s", containerName) + opts := &OpenDevcontainerAppDispatcherOptions{ + Name: containerName, + Wait: false, + } + log.Info("GetDevContainerStatus: Calling AssignDevcontainerGetting2K8sOperator for delete check with opts: %+v", opts) + _, err := AssignDevcontainerGetting2K8sOperator(&ctx, opts) + if err != nil { + log.Info("GetDevContainerStatus: DevcontainerApp not found, considering deleted: %v", err) + realTimeStatus = 10 // 已删除 + } else { + log.Info("GetDevContainerStatus: DevcontainerApp still exists, not deleted yet") + } + // 已在外部通过 DeleteDevContainer 触发,此处仅检查状态 } else { isContainerNotFound, err := IsContainerNotFound(ctx, containerName) if err != nil { @@ -259,6 +337,22 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string, default: log.Info("other status") } + // K8s: 仅在 Ready 后才返回 4;否则维持/降为 3 + if cfg.Section("k8s").Key("ENABLE").Value() == "true" && (status == 3 || status == 4) { + opts := &OpenDevcontainerAppDispatcherOptions{ + Name: containerName, + Wait: false, + } + app, err := AssignDevcontainerGetting2K8sOperator(&ctx, opts) + if err != nil || app == nil { + // 获取不到 CR 或出错时,保守认为未就绪 + realTimeStatus = 3 + } else if app.Status.Ready { + realTimeStatus = 4 + } else { + realTimeStatus = 3 + } + } //状态更新 if realTimeStatus != status { if realTimeStatus == 10 { @@ -289,6 +383,7 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string, return "", err } } + log.Info("GetDevContainerStatus: Final realTimeStatus: %d, returning status string", realTimeStatus) return fmt.Sprintf("%d", realTimeStatus), nil } func CreateDevContainer(ctx context.Context, repo *repo.Repository, doer *user.User, publicKeyList []string, isWebTerminal bool) error { @@ -330,7 +425,28 @@ func CreateDevContainer(ctx context.Context, repo *repo.Repository, doer *user.U go func() { otherCtx := context.Background() if cfg.Section("k8s").Key("ENABLE").Value() == "true" { - //k8s的逻辑 + // K8s 模式:直接调用 K8s Operator 创建 DevContainer + configurationString, err := GetDevcontainerConfigurationString(otherCtx, repo) + if err != nil { + log.Info("CreateDevContainer: 读取 devcontainer 配置失败: %v", err) + return + } + configurationModel, err := UnmarshalDevcontainerConfigContent(configurationString) + if err != nil { + log.Info("CreateDevContainer: 解析 devcontainer 配置失败: %v", err) + return + } + + newDTO := &CreateDevcontainerDTO{ + Devcontainer: newDevcontainer, + SSHPublicKeyList: publicKeyList, + GitRepositoryURL: strings.TrimSuffix(setting.AppURL, "/") + repo.Link(), + Image: configurationModel.Image, + } + if err := AssignDevcontainerCreation2K8sOperator(&otherCtx, newDTO); err != nil { + log.Error("CreateDevContainer: K8s 创建失败: %v", err) + return + } } else { imageName, err := CreateDevContainerByDockerCommand(otherCtx, &newDevcontainer, repo, publicKeyList) if err != nil { @@ -367,7 +483,9 @@ func DeleteDevContainer(ctx context.Context, userID, repoID int64) error { go func() { otherCtx := context.Background() if cfg.Section("k8s").Key("ENABLE").Value() == "true" { - //k8s的逻辑 + // k8s 模式:调用 K8s Operator 删除 DevContainer 资源 + devList := []devcontainer_models.Devcontainer{devContainerInfo} + _ = AssignDevcontainerDeletion2K8sOperator(&otherCtx, &devList) } else { err = DeleteDevContainerByDocker(otherCtx, devContainerInfo.Name) @@ -402,7 +520,15 @@ func RestartDevContainer(ctx context.Context, userID, repoID int64) error { go func() { otherCtx := context.Background() if cfg.Section("k8s").Key("ENABLE").Value() == "true" { - //k8s的逻辑 + // k8s 模式:调用 K8s Operator 重启 DevContainer + vo := &DevcontainerVO{ + DevContainerName: devContainerInfo.Name, + UserId: userID, + RepoId: repoID, + } + if err := AssignDevcontainerRestart2K8sOperator(&otherCtx, vo); err != nil { + log.Error("RestartDevContainer: K8s 重启失败: %v", err) + } } else { err = RestartDevContainerByDocker(otherCtx, devContainerInfo.Name) if err != nil { @@ -436,7 +562,15 @@ func StopDevContainer(ctx context.Context, userID, repoID int64) error { go func() { otherCtx := context.Background() if cfg.Section("k8s").Key("ENABLE").Value() == "true" { - //k8s的逻辑 + // k8s 模式:调用 K8s Operator 停止 DevContainer + vo := &DevcontainerVO{ + DevContainerName: devContainerInfo.Name, + UserId: userID, + RepoId: repoID, + } + if err := AssignDevcontainerStop2K8sOperator(&otherCtx, vo); err != nil { + log.Error("StopDevContainer: K8s 停止失败: %v", err) + } } else { err = StopDevContainerByDocker(otherCtx, devContainerInfo.Name) if err != nil { @@ -891,7 +1025,20 @@ func Get_IDE_TerminalURL(ctx *gitea_context.Context, doer *user.User, repo *gite var port string if cfg.Section("k8s").Key("ENABLE").Value() == "true" { - + // K8s 环境:通过 DevcontainerApp 的 NodePort 作为 SSH 端口 + apiRequestCtx := ctx.Req.Context() + opts := &OpenDevcontainerAppDispatcherOptions{ + Name: devContainerInfo.Name, + Wait: false, + } + devcontainerApp, err := AssignDevcontainerGetting2K8sOperator(&apiRequestCtx, opts) + if err != nil { + return "", err + } + if devcontainerApp == nil || devcontainerApp.Status.NodePortAssigned == 0 { + return "", fmt.Errorf("k8s DevcontainerApp 未就绪或未分配 NodePort: %s", devContainerInfo.Name) + } + port = fmt.Sprintf("%d", devcontainerApp.Status.NodePortAssigned) } else { mappedPort, err := docker_module.GetMappedPort(ctx, devContainerInfo.Name, "22") if err != nil { diff --git a/services/devcontainer/k8s_agent.go b/services/devcontainer/k8s_agent.go new file mode 100644 index 0000000000..7f7f5664d9 --- /dev/null +++ b/services/devcontainer/k8s_agent.go @@ -0,0 +1,891 @@ +package devcontainer + +import ( + "bytes" + "context" + "fmt" + "strings" + "time" + + "code.gitea.io/gitea/models/db" + devcontainer_model "code.gitea.io/gitea/models/devcontainer" + devcontainer_dto "code.gitea.io/gitea/modules/k8s" + devcontainer_k8s_agent_module "code.gitea.io/gitea/modules/k8s" + k8s_api_v1 "code.gitea.io/gitea/modules/k8s/api/devcontainer/v1" + "code.gitea.io/gitea/modules/k8s/errors" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + v1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/remotecommand" + "k8s.io/kubectl/pkg/scheme" + // Istio 资源改为 dynamic/unstructured,不再直接使用 typed API +) + +// 为 K8s Agent 暴露所需的 DTO 类型,便于 K8s 分支创建/查询入口使用 +type CreateDevcontainerDTO struct { + devcontainer_model.Devcontainer + SSHPublicKeyList []string + GitRepositoryURL string + Image string + DockerfileContent string + DevcontainerPort uint16 +} + +type OpenDevcontainerAppDispatcherOptions struct { + Name string `json:"name"` + Wait bool `json:"wait"` + Status uint16 + Port uint16 + UserPublicKey string + RepoID int64 + UserID int64 +} + +var k8sGroupVersionResource = schema.GroupVersionResource{ + Group: "devcontainer.devstar.cn", + Version: "v1", + Resource: "devcontainerapps", +} + +type ErrIllegalK8sAgentParams struct { + FieldNameList []string +} + +func (err ErrIllegalK8sAgentParams) Error() string { + return fmt.Sprintf("Illegal Params: %v", err.FieldNameList) +} + +// AssignDevcontainerGetting2K8sOperator 获取 k8s CRD 资源 DevcontainerApp 最新状态(需要根据用户传入的 wait 参数决定是否要阻塞等待 DevContainer 就绪) +func AssignDevcontainerGetting2K8sOperator(ctx *context.Context, opts *OpenDevcontainerAppDispatcherOptions) (*k8s_api_v1.DevcontainerApp, error) { + log.Info("AssignDevcontainerGetting2K8sOperator: Starting lookup for container: %s, wait=%v", + opts.Name, opts.Wait) + + // 0. 检查参数 + if ctx == nil || opts == nil || len(opts.Name) == 0 { + return nil, ErrIllegalK8sAgentParams{ + FieldNameList: []string{"ctx", "opts", "opts.Name"}, + } + } + + // 1. 获取 Dynamic Client + ctxVal := *ctx + client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "") + if err != nil { + // 层层返回错误,结束数据库事务 + return nil, errors.ErrOperateDevcontainer{ + Action: "Connect to k8s API Server", + Message: err.Error(), + } + } + log.Info("AssignDevcontainerGetting2K8sOperator: K8s client created successfully") + + // 2. 调用 modules 层 k8s Agent 获取 k8s CRD 资源 DevcontainerApp + optsGetDevcontainer := &devcontainer_dto.GetDevcontainerOptions{ + GetOptions: metav1.GetOptions{}, + Name: opts.Name, + Namespace: setting.DevContainerConfig.Namespace, + Wait: opts.Wait, + } + log.Info("AssignDevcontainerGetting2K8sOperator: Retrieving DevcontainerApp %s in namespace %s (wait=%v)", + opts.Name, setting.DevContainerConfig.Namespace, opts.Wait) + devcontainerApp, err := devcontainer_k8s_agent_module.GetDevcontainer(ctxVal, client, optsGetDevcontainer) + if err != nil { + log.Error("AssignDevcontainerGetting2K8sOperator: Failed to get DevcontainerApp: %v", err) + return nil, errors.ErrOperateDevcontainer{ + Action: fmt.Sprintf("Open Devcontainer '%s' (wait=%v)", opts.Name, opts.Wait), + Message: err.Error(), + } + } + log.Info("AssignDevcontainerGetting2K8sOperator: DevcontainerApp retrieved successfully - Name: %s, NodePort: %d, Ready: %v", + devcontainerApp.Name, devcontainerApp.Status.NodePortAssigned, devcontainerApp.Status.Ready) + + // 添加额外端口的日志 + if len(devcontainerApp.Status.ExtraPortsAssigned) > 0 { + for i, port := range devcontainerApp.Status.ExtraPortsAssigned { + log.Info("AssignDevcontainerGetting2K8sOperator: Extra port %d - Name: %s, NodePort: %d, ContainerPort: %d", + i, port.Name, port.NodePort, port.ContainerPort) + } + } else { + log.Info("AssignDevcontainerGetting2K8sOperator: No extra ports found for DevcontainerApp %s", devcontainerApp.Name) + } + + // 3. 成功获取最新的 DevcontainerApp,返回 + return devcontainerApp, nil +} + +// 补充笔记: modules/ 与 services/ 两个目录中的 k8s Agent 区别是什么? +// - modules/ 与 k8s API Server 交互密切相关 +// - services/ 进行了封装,简化用户界面使用 + +func AssignDevcontainerDeletion2K8sOperator(ctx *context.Context, devcontainersList *[]devcontainer_model.Devcontainer) error { + log.Info("AssignDevcontainerDeletion2K8sOperator: Starting Deletion for containers") + // 1. 获取 Dynamic Client + ctxVal := *ctx + client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "") + if err != nil { + // 层层返回错误,结束数据库事务 + return err + } + + // 获取标准 Kubernetes 客户端,用于删除 Ingress + stdClient, err := getStandardKubernetesClient() + if err != nil { + log.Warn("AssignDevcontainerDeletion2K8sOperator: 获取标准 K8s 客户端失败: %v", err) + // 继续执行,不阻止主流程 + } else { + // 先删除与 DevContainer 相关的 Ingress 资源 + for _, devcontainer := range *devcontainersList { + ingressName := fmt.Sprintf("%s-ttyd-ingress", devcontainer.Name) + log.Info("AssignDevcontainerDeletion2K8sOperator: 删除 Ingress %s", ingressName) + + err := stdClient.NetworkingV1().Ingresses(setting.DevContainerConfig.Namespace).Delete(*ctx, ingressName, metav1.DeleteOptions{}) + if err != nil { + if k8serrors.IsNotFound(err) { + // Ingress 已经不存在,视为正常情况 + log.Info("AssignDevcontainerDeletion2K8sOperator: Ingress %s 不存在,跳过删除", ingressName) + } else { + log.Warn("AssignDevcontainerDeletion2K8sOperator: 删除 Ingress %s 失败: %v", ingressName, err) + // 继续执行,不阻止主流程 + } + } else { + log.Info("AssignDevcontainerDeletion2K8sOperator: 成功删除 Ingress %s", ingressName) + } + } + } + + // 2. 调用 modules 层 k8s Agent,执行删除资源 + opts := &devcontainer_dto.DeleteDevcontainerOptions{ + DeleteOptions: metav1.DeleteOptions{}, + Namespace: setting.DevContainerConfig.Namespace, + } + if devcontainersList == nil || len(*devcontainersList) == 0 { + return fmt.Errorf("delete devcontainer in namespace '%s': %s", opts.Namespace, "the DevContainer list is empty") + } + // 3. 遍历列表删除 DevContainer,如果删除出错,交由 module 层打印日志,交由管理员手动处理 + for _, devcontainer := range *devcontainersList { + opts.Name = devcontainer.Name + _ = devcontainer_k8s_agent_module.DeleteDevcontainer(ctxVal, client, opts) + + // 删除对应的 VirtualService + if err := deleteDevContainerWebTerminalVirtualService(ctx, devcontainer.Name); err != nil { + log.Warn("AssignDevcontainerDeletion2K8sOperator: 删除 VirtualService 失败 for DevContainer %s: %v", devcontainer.Name, err) + // 不阻止主流程,只记录警告 + } else { + log.Info("AssignDevcontainerDeletion2K8sOperator: 成功删除 VirtualService for DevContainer: %s", devcontainer.Name) + } + } + return nil +} + +// 补充笔记: modules/ 与 services/ 两个目录中的 k8s Agent 区别是什么? +// - modules/ 与 k8s API Server 交互密切相关 +// - services/ 进行了封装,简化用户界面使用 + +// AssignDevcontainerCreation2K8sOperator 将 DevContainer 资源创建任务派遣至 k8s Operator,同时根据结果更新 NodePort +// +// 注意:本方法仍然在数据库事务中,因此不适合执行长时间操作,故需要后期异步判断 DevContainer 是否就绪 +func AssignDevcontainerCreation2K8sOperator(ctx *context.Context, newDevContainer *CreateDevcontainerDTO) error { + log.Info("AssignDevcontainerCreation2K8sOperator: Starting creation for container: %s", newDevContainer.Name) + log.Info("AssignDevcontainerCreation2K8sOperator: Container details - Image: %s, RepoURL: %s, SSHKeys: %d", + newDevContainer.Image, newDevContainer.GitRepositoryURL, len(newDevContainer.SSHPublicKeyList)) + + // 1. 获取 Dynamic Client + ctxVal := *ctx + client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "") + if err != nil { + // 层层返回错误,结束数据库事务 + return err + } + + // 1.1:插入 devcontainer_output 记录 + dbEngine := db.GetEngine(*ctx) + + // 更新状态为 1:正在拉取镜像 + _, err = dbEngine.Table("devcontainer"). + Where("user_id = ? AND repo_id = ? ", newDevContainer.UserId, newDevContainer.RepoId). + Update(&devcontainer_model.Devcontainer{DevcontainerStatus: 1}) + if err != nil { + log.Info("Failed to update status to 1: %v", err) + } + + // 插入拉取镜像记录 + if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{ + Output: "Pulling image for K8s container: " + newDevContainer.Image, + ListId: 0, + Status: "success", // 设为 success 以满足 created 变量的条件 + UserId: newDevContainer.UserId, + RepoId: newDevContainer.RepoId, + Command: "Pull Image", + }); err != nil { + log.Info("Failed to insert Pull Image record: %v", err) + // 不返回错误,继续执行 + } + + // 更新状态为 2:正在创建和启动容器 + _, err = dbEngine.Table("devcontainer"). + Where("user_id = ? AND repo_id = ? ", newDevContainer.UserId, newDevContainer.RepoId). + Update(&devcontainer_model.Devcontainer{DevcontainerStatus: 2}) + if err != nil { + log.Info("Failed to update status to 2: %v", err) + } + + // 插入初始化工作区记录 (满足 created = true 的关键条件) + if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{ + Output: "Initializing workspace in Kubernetes...", + Status: "success", // 必须为 success + UserId: newDevContainer.UserId, + RepoId: newDevContainer.RepoId, + Command: "Initialize Workspace", + ListId: 1, // ListId > 0 且 Status = success 是 created = true 的条件 + }); err != nil { + log.Info("Failed to insert Initialize Workspace record: %v", err) + // 不返回错误,继续执行 + } + + // 更新状态为 3:容器安装必要工具 + _, err = dbEngine.Table("devcontainer"). + Where("user_id = ? AND repo_id = ? ", newDevContainer.UserId, newDevContainer.RepoId). + Update(&devcontainer_model.Devcontainer{DevcontainerStatus: 3}) + if err != nil { + log.Info("Failed to update status to 3: %v", err) + } + + // 插入初始化 DevStar 记录 + if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{ + Output: "Initializing DevStar in Kubernetes...", + Status: "success", + UserId: newDevContainer.UserId, + RepoId: newDevContainer.RepoId, + Command: "Initialize DevStar", + ListId: 2, + }); err != nil { + log.Info("Failed to insert Initialize DevStar record: %v", err) + // 不返回错误,继续执行 + } + + // 插入 postCreateCommand 记录 + if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{ + Output: "Running post-create commands in Kubernetes...", + Status: "success", + UserId: newDevContainer.UserId, + RepoId: newDevContainer.RepoId, + Command: "Run postCreateCommand", + ListId: 3, + }); err != nil { + log.Info("Failed to insert Run postCreateCommand record: %v", err) + // 不返回错误,继续执行 + } + + // 添加 ttyd 端口配置 - WebTerminal 功能 + log.Info("AssignDevcontainerCreation2K8sOperator: Adding ttyd port configuration (7681)") + extraPorts := []k8s_api_v1.ExtraPortSpec{ + { + Name: "ttyd", + ContainerPort: 7681, // ttyd 默认端口 + ServicePort: 7681, + }, + } + + command := []string{ + "/bin/bash", + "-c", + "export DEBIAN_FRONTEND=noninteractive && " + + "apt-get update -y && " + + "apt-get install -y ssh && " + + // 改为条件生成:只有在密钥不存在时才生成 + "if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then " + + " echo 'Generating SSH host keys...' && " + + " ssh-keygen -A && " + + " echo 'SSH host keys generated' ; " + + "else " + + " echo 'SSH host keys already exist' ; " + + "fi && " + + "mkdir -p /var/run/sshd && " + + "/usr/sbin/sshd && " + + "if [ -f /ttyd-shared/ttyd ]; then " + + "mkdir -p /data/workspace && " + + "cd /data/workspace && " + + "/ttyd-shared/ttyd -p 7681 -i 0.0.0.0 --writable bash > /tmp/ttyd.log 2>&1 & " + + "fi && " + + "while true; do sleep 60; done", + } + log.Info("AssignDevcontainerCreation2K8sOperator: Command includes ttyd installation and startup") + + // 2. 调用 modules 层 k8s Agent,执行创建资源 + opts := &devcontainer_dto.CreateDevcontainerOptions{ + CreateOptions: metav1.CreateOptions{}, + Name: newDevContainer.Name, + Namespace: setting.DevContainerConfig.Namespace, + Image: newDevContainer.Image, + /** + * 配置 Kubernetes 主容器启动命令注意事项: + * 1. 确保 Image 中已安装 OpenSSH Server + * 2. 容器启动后必须拉起 OpenSSH 后台服务 + * 3. 请勿使用 sleep infinity 或者 tail -f /dev/null 等无限等待命令, + * 可以考虑无限循环 sleep 60s,能够防止 k8s 中容器先变成 Completed 然后变为 CrashLoopBackOff + * 也可以防止造成大量僵尸()进程: + * $ ps aux | grep "" # 列举僵尸进程 + * USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND + * pollina+ 2336 0.0 0.0 0 0 ? Z 17:22 0:00 [sshd] + * pollina+ 10986 0.0 0.0 0 0 ? Z 16:12 0:00 [sshd] + * pollina+ 24722 0.0 0.0 0 0 ? Z 18:36 0:00 [sshd] + * pollina+ 26773 0.0 0.0 0 0 ? Z 18:37 0:00 [sshd] + * $ ubuntu@node2:~$ ps o ppid 2336 10986 24722 26773 # 查询僵尸进程父进程PID + * PPID + * 21826 + * $ ps aux | grep # 列举僵尸进程父进程详情 + * USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND + * root 21826 0.0 0.0 2520 408 ? Ss 18:36 0:00 sleep infinity + */ + CommandList: command, + ContainerPort: 22, + ServicePort: 22, + SSHPublicKeyList: newDevContainer.SSHPublicKeyList, + GitRepositoryURL: newDevContainer.GitRepositoryURL, + ExtraPorts: extraPorts, // 添加额外端口配置 + } + + // 2. 创建成功,取回集群中的 DevContainer + log.Info("AssignDevcontainerCreation2K8sOperator: Creating DevcontainerApp %s in namespace %s", + opts.Name, opts.Namespace) + devcontainerInCluster, err := devcontainer_k8s_agent_module.CreateDevcontainer(ctxVal, client, opts) + if err != nil { + log.Error("AssignDevcontainerCreation2K8sOperator: Failed to create DevcontainerApp: %v", err) + return err + } + log.Info("AssignDevcontainerCreation2K8sOperator: DevcontainerApp created successfully - Name: %s", + devcontainerInCluster.Name) + + // 不再在创建后立即置为 4,保持为 3;待 Pod Ready 后由 GetDevContainerStatus 升级为 4 + + // 3. 处理 NodePort - 检查是否为0(尚未分配) + nodePort := devcontainerInCluster.Status.NodePortAssigned + + if nodePort == 0 { + log.Info("AssignDevcontainerCreation2K8sOperator: NodePort not yet assigned, starting async updater for %s", + devcontainerInCluster.Name) + + // 将端口设为0,数据库中记录特殊标记 + newDevContainer.DevcontainerPort = 0 + + // 记录容器已创建,但端口待更新 + log.Info("DevContainer created in cluster - Name: %s, NodePort: pending assignment", + devcontainerInCluster.Name) + + // 启动异步任务来更新端口 + go updateNodePortAsync(devcontainerInCluster.Name, + setting.DevContainerConfig.Namespace, + newDevContainer.UserId, + newDevContainer.RepoId) + } else { + log.Info("AssignDevcontainerCreation2K8sOperator: NodePort %d assigned immediately to %s", + nodePort, devcontainerInCluster.Name) + + // 端口已分配,直接使用 + newDevContainer.DevcontainerPort = nodePort + log.Info("DevContainer created in cluster - Name: %s, NodePort: %d", + devcontainerInCluster.Name, nodePort) + } + + log.Info("DevContainer created in cluster - Name: %s, NodePort: %d", + devcontainerInCluster.Name, + devcontainerInCluster.Status.NodePortAssigned) + + // 为 ttyd 服务创建 Istio Gateway 和 VirtualService + log.Info("AssignDevcontainerCreation2K8sOperator: 开始创建 Istio 资源 for DevContainer: %s", devcontainerInCluster.Name) + + // 1. 确保 Gateway 存在 + if err := createDevContainerWebTerminalGateway(ctx); err != nil { + log.Warn("AssignDevcontainerCreation2K8sOperator: 创建 Gateway 失败: %v", err) + // 不阻止主流程,只记录警告 + } else { + log.Info("AssignDevcontainerCreation2K8sOperator: Gateway 创建成功") + } + + // 2. 创建 VirtualService + if err := createDevContainerWebTerminalVirtualService(ctx, devcontainerInCluster.Name); err != nil { + log.Warn("AssignDevcontainerCreation2K8sOperator: 创建 VirtualService 失败: %v", err) + // 不阻止主流程,只记录警告 + } else { + log.Info("AssignDevcontainerCreation2K8sOperator: VirtualService 创建成功") + } + + // 4. 层层返回 nil,自动提交数据库事务,完成 DevContainer 创建 + return nil +} + +// AssignDevcontainerRestart2K8sOperator 将 DevContainer 重启任务派遣至 K8s 控制器 +func AssignDevcontainerRestart2K8sOperator(ctx *context.Context, opts *DevcontainerVO) error { + log.Info("AssignDevcontainerRestart2K8sOperator: Starting restart for container: %s", opts.DevContainerName) + + // 1. 获取 Dynamic Client + ctxVal := *ctx + client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "") + if err != nil { + log.Error("Failed to get Kubernetes client: %v", err) + return err + } + + // 2. 通过打补丁方式实现重启 - 更新注解以触发控制器重新部署 Pod + // 创建补丁,添加或更新 restartedAt 注解,同时确保 desiredReplicas 为 1 + patchData := fmt.Sprintf(`{ + "metadata": { + "annotations": { + "devstar.io/restartedAt": "%s", + "devstar.io/desiredReplicas": "1" + } + } + }`, time.Now().Format(time.RFC3339)) + log.Info("AssignDevcontainerRestart2K8sOperator: Applying patch to restart container %s", + opts.DevContainerName) + log.Debug("AssignDevcontainerRestart2K8sOperator: Patch data: %s", patchData) + + // 应用补丁到 DevcontainerApp CRD + _, err = client.Resource(k8sGroupVersionResource). + Namespace(setting.DevContainerConfig.Namespace). + Patch(ctxVal, opts.DevContainerName, types.MergePatchType, []byte(patchData), metav1.PatchOptions{}) + + if err != nil { + log.Error("Failed to patch DevcontainerApp for restart: %v", err) + return fmt.Errorf("restart k8s devcontainer '%s' failed: %v", opts.DevContainerName, err) + } + + // 记录重启操作日志 + log.Info("DevContainer restarted: %s", opts.DevContainerName) + log.Info("AssignDevcontainerRestart2K8sOperator: Restart patch applied successfully for %s", + opts.DevContainerName) + + // 将重启操作记录到数据库 + dbEngine := db.GetEngine(*ctx) + _, err = dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{ + Output: fmt.Sprintf("Restarting K8s DevContainer %s", opts.DevContainerName), + Status: "success", + UserId: opts.UserId, + RepoId: opts.RepoId, + Command: "Restart DevContainer", + ListId: 0, + }) + if err != nil { + log.Warn("Failed to insert restart record: %v", err) + } + + return nil +} + +// AssignDevcontainerStop2K8sOperator 将 DevContainer 停止任务派遣至 K8s 控制器 +func AssignDevcontainerStop2K8sOperator(ctx *context.Context, opts *DevcontainerVO) error { + // 1. 获取 Dynamic Client + ctxVal := *ctx + client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "") + if err != nil { + log.Error("Failed to get Kubernetes client: %v", err) + return err + } + + // 2. 通过打补丁方式实现停止 - 添加停止注解 + // 创建补丁,添加或更新 stopped 和 desiredReplicas 注解 + patchData := fmt.Sprintf(`{ + "metadata": { + "annotations": { + "devstar.io/stoppedAt": "%s", + "devstar.io/desiredReplicas": "0" + } + } + }`, time.Now().Format(time.RFC3339)) + + // 应用补丁到 DevcontainerApp CRD + _, err = client.Resource(k8sGroupVersionResource). + Namespace(setting.DevContainerConfig.Namespace). + Patch(ctxVal, opts.DevContainerName, types.MergePatchType, []byte(patchData), metav1.PatchOptions{}) + + if err != nil { + log.Error("Failed to patch DevcontainerApp for stop: %v", err) + return fmt.Errorf("stop k8s devcontainer '%s' failed: %v", opts.DevContainerName, err) + } + + // 记录停止操作日志 + log.Info("DevContainer stopped: %s", opts.DevContainerName) + + // 将停止操作记录到数据库 + dbEngine := db.GetEngine(*ctx) + _, err = dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{ + Output: fmt.Sprintf("Stopping K8s DevContainer %s", opts.DevContainerName), + Status: "success", + UserId: opts.UserId, + RepoId: opts.RepoId, + Command: "Stop DevContainer", + ListId: 0, + }) + if err != nil { + // 只记录错误,不影响主流程返回结果 + log.Warn("Failed to insert stop record: %v", err) + } + + return nil +} + +// 异步更新 NodePort 的辅助函数 +func updateNodePortAsync(containerName string, namespace string, userId, repoId int64) { + log.Info("updateNodePortAsync: Starting for container: %s in namespace: %s", containerName, namespace) + log.Info("updateNodePortAsync: Waiting 20 seconds for K8s controller to assign port") + + // 等待K8s控制器完成端口分配 + time.Sleep(20 * time.Second) + + // 创建新的上下文和客户端 + ctx := context.Background() + client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctx, nil, "") + if err != nil { + log.Error("Failed to get K8s client in async updater: %v", err) + return + } + log.Info("updateNodePortAsync: K8s client created successfully") + + // 尝试最多10次获取端口 + for i := 0; i < 10; i++ { + log.Info("updateNodePortAsync: Attempt %d/10 to retrieve NodePort for %s", i+1, containerName) + getOpts := &devcontainer_k8s_agent_module.GetDevcontainerOptions{ + GetOptions: metav1.GetOptions{}, + Name: containerName, + Namespace: namespace, + Wait: false, + } + + devcontainer, err := devcontainer_k8s_agent_module.GetDevcontainer(ctx, client, getOpts) + if err == nil && devcontainer != nil && devcontainer.Status.NodePortAssigned > 0 { + log.Info("updateNodePortAsync: Success! Found NodePort %d for %s", + devcontainer.Status.NodePortAssigned, containerName) + // 获取到正确的端口,更新数据库 + realNodePort := devcontainer.Status.NodePortAssigned + + // 记录 ttyd 端口信息到日志 + if len(devcontainer.Status.ExtraPortsAssigned) > 0 { + for _, portInfo := range devcontainer.Status.ExtraPortsAssigned { + log.Info("Found extra port for %s: name=%s, nodePort=%d, containerPort=%d", + containerName, portInfo.Name, portInfo.NodePort, portInfo.ContainerPort) + } + } + + log.Info("Found real NodePort %d for container %s, updating database record", + realNodePort, containerName) + + engine := db.GetEngine(ctx) + _, err := engine.Table("devcontainer"). + Where("user_id = ? AND repo_id = ?", userId, repoId). + Update(map[string]interface{}{ + "devcontainer_port": realNodePort, + }) + + if err != nil { + log.Error("Failed to update NodePort in database: %v", err) + } else { + log.Info("Successfully updated NodePort in database to %d", realNodePort) + } + + return + } + + log.Info("updateNodePortAsync: Port not yet assigned, waiting 5 seconds before next attempt") + time.Sleep(5 * time.Second) + } + + log.Warn("updateNodePortAsync: Failed to retrieve real NodePort after multiple attempts") +} + +// 获取标准 Kubernetes 客户端 +func getStandardKubernetesClient() (*kubernetes.Clientset, error) { + // 使用与 GetKubernetesClient 相同的逻辑获取配置 + config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile) + if err != nil { + // 如果集群外配置失败,尝试集群内配置 + log.Warn("Failed to obtain Kubernetes config outside of cluster: %v", err) + config, err = rest.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("获取 K8s 配置失败 (集群内外均失败): %v", err) + } + } + + // 创建标准客户端 + stdClient, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("创建标准 K8s 客户端失败: %v", err) + } + + return stdClient, nil +} + +// 创建 DevContainer WebTerminal Gateway +func createDevContainerWebTerminalGateway(ctx *context.Context) error { + log.Info("createDevContainerWebTerminalGateway: 开始创建 DevContainer WebTerminal Gateway") + + // 获取 Dynamic Client + ctxVal := *ctx + client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "") + if err != nil { + return fmt.Errorf("获取 K8s 客户端失败: %v", err) + } + + gatewayName := "devcontainer-webterminal-gateway" + namespace := setting.DevContainerConfig.Namespace + + // 检查 Gateway 是否已存在 + gwGVR := schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1", Resource: "gateways"} + if _, err := client.Resource(gwGVR).Namespace(namespace).Get(ctxVal, gatewayName, metav1.GetOptions{}); err == nil { + log.Info("createDevContainerWebTerminalGateway: Gateway 已存在: %s", gatewayName) + return nil + } else if !k8serrors.IsNotFound(err) { + return fmt.Errorf("检查 Gateway 失败: %v", err) + } + + // 从配置中读取域名 + cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf) + if err != nil { + return fmt.Errorf("加载配置文件失败: %v", err) + } + + domain := cfg.Section("server").Key("DOMAIN").Value() + + // 使用 Unstructured 定义 Gateway(HTTP-only) + gw := &unstructured.Unstructured{Object: map[string]interface{}{ + "apiVersion": "networking.istio.io/v1", + "kind": "Gateway", + "metadata": map[string]interface{}{ + "name": gatewayName, + "namespace": namespace, + "labels": map[string]interface{}{ + "app.kubernetes.io/name": "devcontainer-webterminal", + "app.kubernetes.io/component": "gateway", + "app.kubernetes.io/managed-by": "devstar", + }, + }, + "spec": map[string]interface{}{ + "selector": map[string]interface{}{"istio": "ingressgateway"}, + "servers": []interface{}{ + map[string]interface{}{ + "port": map[string]interface{}{"number": 80, "name": "http", "protocol": "HTTP"}, + "hosts": []interface{}{domain, "*"}, + }, + }, + }, + }} + if _, err := client.Resource(gwGVR).Namespace(namespace).Create(ctxVal, gw, metav1.CreateOptions{}); err != nil { + return fmt.Errorf("创建 Gateway 失败: %v", err) + } + + log.Info("createDevContainerWebTerminalGateway: 成功创建 Gateway: %s", gatewayName) + return nil +} + +// 创建 DevContainer WebTerminal VirtualService +func createDevContainerWebTerminalVirtualService(ctx *context.Context, devcontainerName string) error { + log.Info("createDevContainerWebTerminalVirtualService: 开始创建 VirtualService for DevContainer: %s", devcontainerName) + + // 获取 Dynamic Client + ctxVal := *ctx + client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "") + if err != nil { + return fmt.Errorf("获取 K8s 客户端失败: %v", err) + } + + vsName := devcontainerName + "-webterminal-vs" + namespace := setting.DevContainerConfig.Namespace + + // 从配置中读取域名 + cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf) + if err != nil { + return fmt.Errorf("加载配置文件失败: %v", err) + } + + domain := cfg.Section("server").Key("DOMAIN").Value() + // VirtualService 的 hosts 只能二选一:有 DOMAIN 用 [DOMAIN],否则用 ["*"] + var vsHosts []interface{} + if domain != "" { + vsHosts = []interface{}{domain} + } else { + vsHosts = []interface{}{"*"} + } + // 从容器名称中提取用户名和仓库名 + parts := strings.Split(devcontainerName, "-") + var username, repoName string + if len(parts) >= 2 { + username = parts[0] + repoName = parts[1] + } else { + username = "unknown" + repoName = "unknown" + } + + // 构建访问路径 + path := fmt.Sprintf("/%s/%s/dev-container-webterminal", username, repoName) + + // 使用 Unstructured 定义 VS,并增加路径重写到根路径 + vsGVR := schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1", Resource: "virtualservices"} + vs := &unstructured.Unstructured{Object: map[string]interface{}{ + "apiVersion": "networking.istio.io/v1", + "kind": "VirtualService", + "metadata": map[string]interface{}{ + "name": vsName, + "namespace": namespace, + "labels": map[string]interface{}{ + "app.kubernetes.io/name": "devcontainer-webterminal", + "app.kubernetes.io/component": "virtualservice", + "app.kubernetes.io/managed-by": "devstar", + "devcontainer-name": devcontainerName, + }, + }, + "spec": map[string]interface{}{ + "hosts": vsHosts, + "gateways": []interface{}{"devcontainer-webterminal-gateway"}, + "http": []interface{}{ + map[string]interface{}{ + "match": []interface{}{map[string]interface{}{"uri": map[string]interface{}{"prefix": path}}}, + "rewrite": map[string]interface{}{"uri": "/"}, + "route": []interface{}{ + map[string]interface{}{ + "destination": map[string]interface{}{ + "host": devcontainerName + "-svc", + "port": map[string]interface{}{"number": 7681}, + }, + }, + }, + "timeout": "3600s", + "retries": map[string]interface{}{ + "attempts": 3, + "perTryTimeout": "30s", + "retryOn": "5xx,gateway-error,connect-failure", + }, + }, + }, + }, + }} + if _, err := client.Resource(vsGVR).Namespace(namespace).Create(ctxVal, vs, metav1.CreateOptions{}); err != nil { + return fmt.Errorf("创建 VirtualService 失败: %v", err) + } + + log.Info("createDevContainerWebTerminalVirtualService: 成功创建 VirtualService: %s, 路径: %s", vsName, path) + return nil +} + +// 删除 DevContainer WebTerminal VirtualService +func deleteDevContainerWebTerminalVirtualService(ctx *context.Context, devcontainerName string) error { + log.Info("deleteDevContainerWebTerminalVirtualService: 开始删除 VirtualService for DevContainer: %s", devcontainerName) + + // 获取 Dynamic Client + ctxVal := *ctx + client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "") + if err != nil { + return fmt.Errorf("获取 K8s 客户端失败: %v", err) + } + + vsName := devcontainerName + "-webterminal-vs" + namespace := setting.DevContainerConfig.Namespace + + vsGVR := schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1", Resource: "virtualservices"} + if err := client.Resource(vsGVR).Namespace(namespace).Delete(ctxVal, vsName, metav1.DeleteOptions{}); err != nil { + if k8serrors.IsNotFound(err) { + log.Info("deleteDevContainerWebTerminalVirtualService: VirtualService 不存在,无需删除: %s", vsName) + return nil + } + return fmt.Errorf("删除 VirtualService 失败: %v", err) + } + + log.Info("deleteDevContainerWebTerminalVirtualService: 成功删除 VirtualService: %s", vsName) + return nil +} + +// executeCommandInK8sPod 在 K8s Pod 中执行命令的辅助函数 +func executeCommandInK8sPod(ctx *context.Context, client *kubernetes.Clientset, namespace, devcontainerName, containerName string, command []string) error { + log.Info("executeCommandInK8sPod: 开始为 DevContainer %s 查找对应的 Pod", devcontainerName) + + // 1. 首先根据标签选择器查找对应的 Pod + labelSelector := fmt.Sprintf("app=%s", devcontainerName) + pods, err := client.CoreV1().Pods(namespace).List(*ctx, metav1.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil { + log.Error("executeCommandInK8sPod: 查找 Pod 失败: %v", err) + return fmt.Errorf("查找 Pod 失败: %v", err) + } + + if len(pods.Items) == 0 { + log.Error("executeCommandInK8sPod: 未找到 DevContainer %s 对应的 Pod", devcontainerName) + return fmt.Errorf("未找到 DevContainer %s 对应的 Pod", devcontainerName) + } + + // 2. 找到第一个运行中的 Pod + var targetPod *v1.Pod + for i := range pods.Items { + pod := &pods.Items[i] + if pod.Status.Phase == v1.PodRunning { + targetPod = pod + break + } + } + + if targetPod == nil { + log.Error("executeCommandInK8sPod: DevContainer %s 没有运行中的 Pod", devcontainerName) + return fmt.Errorf("DevContainer %s 没有运行中的 Pod", devcontainerName) + } + + podName := targetPod.Name + log.Info("executeCommandInK8sPod: 找到运行中的 Pod: %s, 在容器 %s 中执行命令", + podName, containerName) + + // 3. 执行命令 + req := client.CoreV1().RESTClient().Post(). + Resource("pods"). + Name(podName). + Namespace(namespace). + SubResource("exec"). + Param("container", containerName) + + req.VersionedParams(&v1.PodExecOptions{ + Container: containerName, + Command: command, + Stdin: false, + Stdout: true, + Stderr: true, + TTY: false, + }, scheme.ParameterCodec) + + // 获取 executor + config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile) + if err != nil { + // 如果集群外配置失败,尝试集群内配置 + config, err = rest.InClusterConfig() + if err != nil { + return fmt.Errorf("获取 K8s 配置失败: %v", err) + } + } + + executor, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL()) + if err != nil { + return fmt.Errorf("创建命令执行器失败: %v", err) + } + + // 执行命令 + var stdout, stderr bytes.Buffer + err = executor.StreamWithContext(*ctx, remotecommand.StreamOptions{ + Stdout: &stdout, + Stderr: &stderr, + }) + + if err != nil { + log.Error("executeCommandInK8sPod: 命令执行失败: %v, stderr: %s", + err, stderr.String()) + return fmt.Errorf("命令执行失败: %v, stderr: %s", err, stderr.String()) + } + + log.Info("executeCommandInK8sPod: 命令执行成功, stdout: %s", stdout.String()) + return nil +} diff --git a/services/forms/admin.go b/services/forms/admin.go index a1fd976ec2..abb4219964 100644 --- a/services/forms/admin.go +++ b/services/forms/admin.go @@ -48,8 +48,9 @@ type AdminEditUserForm struct { Restricted bool AllowGitHook bool AllowImportLocal bool - AllowCreateOrganization bool - AllowCreateDevcontainer bool + AllowCreateOrganization bool `form:"allow_create_organization"` + AllowCreateDevcontainer bool `form:"allow_create_devcontainer"` + AllowCreateActRunner bool `form:"allow_create_actrunner"` ProhibitLogin bool Reset2FA bool `form:"reset_2fa"` Visibility structs.VisibleType diff --git a/services/forms/user_form.go b/services/forms/user_form.go index 784a1b6a40..780ebeadf3 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -60,8 +60,9 @@ type InstallForm struct { EnableCaptcha bool RequireSignInView bool DefaultKeepEmailPrivate bool - DefaultAllowCreateOrganization bool - DefaultAllowCreateDevcontainer bool + DefaultAllowCreateOrganization bool `form:"default_allow_create_organization"` + DefaultAllowCreateDevcontainer bool `form:"default_allow_create_devcontainer"` + DefaultAllowCreateActRunner bool `form:"default_allow_create_actrunner"` DefaultEnableTimetracking bool EnableUpdateChecker bool NoReplyAddress string diff --git a/services/runners/runners.go b/services/runners/runners.go index 59bca11bac..8dbfbebe4e 100644 --- a/services/runners/runners.go +++ b/services/runners/runners.go @@ -40,6 +40,7 @@ func checkK8sIsEnable() bool { func RegistRunner(ctx context.Context, token string) error { log.Info("开始注册Runner...") + var err error if checkK8sIsEnable() { err = registK8sRunner(ctx, token) diff --git a/services/user/update.go b/services/user/update.go index 116c4e43a2..cb95f78800 100644 --- a/services/user/update.go +++ b/services/user/update.go @@ -52,6 +52,7 @@ type UpdateOptions struct { DiffViewStyle optional.Option[string] AllowCreateOrganization optional.Option[bool] AllowCreateDevcontainer optional.Option[bool] + AllowCreateActRunner optional.Option[bool] IsActive optional.Option[bool] IsAdmin optional.Option[UpdateOptionField[bool]] EmailNotificationsPreference optional.Option[string] @@ -170,6 +171,11 @@ func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) er cols = append(cols, "allow_create_devcontainer") } + if opts.AllowCreateActRunner.Has() { + u.AllowCreateActRunner = opts.AllowCreateActRunner.Value() + + cols = append(cols, "allow_create_act_runner") + } if opts.RepoAdminChangeTeamAccess.Has() { u.RepoAdminChangeTeamAccess = opts.RepoAdminChangeTeamAccess.Value() diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index e18268f205..d6a801f388 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -155,6 +155,8 @@
{{svg (Iif .Service.DefaultAllowCreateOrganization "octicon-check" "octicon-x")}}
{{ctx.Locale.Tr "admin.config.default_allow_create_devcontainer"}}
{{svg (Iif .Service.DefaultAllowCreateDevcontainer "octicon-check" "octicon-x")}}
+
{{ctx.Locale.Tr "admin.config.default_allow_create_actrunner"}}
+
{{svg (Iif .Service.DefaultAllowCreateActRunner "octicon-check" "octicon-x")}}
{{ctx.Locale.Tr "admin.config.enable_timetracking"}}
{{svg (Iif .Service.EnableTimetracking "octicon-check" "octicon-x")}}
{{if .Service.EnableTimetracking}} diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl index f34150503c..75ab7eef1d 100644 --- a/templates/admin/user/edit.tmpl +++ b/templates/admin/user/edit.tmpl @@ -155,6 +155,13 @@ +
+
+ + +
+
+ {{if .TwoFactorEnabled}}
diff --git a/templates/install.tmpl b/templates/install.tmpl index 76a5ffdc82..8366158486 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -8,7 +8,7 @@
{{template "base/alert" .}} -

{{ctx.Locale.Tr "install.docker_helper" "https://docs.gitea.com/installation/install-with-docker"}}

+

{{ctx.Locale.Tr "install.k8s_helper" "https://www.mengning.com/src/devstar/install/install-k8s"}}

@@ -160,7 +160,7 @@
-
+
{{ctx.Locale.Tr "install.k8s_title"}} @@ -178,7 +178,6 @@
-
@@ -310,6 +309,12 @@
+
+
+ + +
+
diff --git a/templates/shared/actions/runner_list.tmpl b/templates/shared/actions/runner_list.tmpl index 9b2b90874c..5b4708be26 100644 --- a/templates/shared/actions/runner_list.tmpl +++ b/templates/shared/actions/runner_list.tmpl @@ -9,9 +9,11 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}