diff --git a/api/v1alpha1/condition.go b/api/v1alpha1/condition.go new file mode 100644 index 000000000..a2ea5b028 --- /dev/null +++ b/api/v1alpha1/condition.go @@ -0,0 +1,181 @@ +/* +Copyright 2023 KubeAGI. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "sort" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// A ConditionType represents a condition a resource could be in. +type ConditionType string + +// Some common Condition types. +const ( + // TypeReady resources are believed to be ready to handle work. + TypeReady ConditionType = "Ready" + // TypeUnknown resources are unknown to the system + TypeUnknown ConditionType = "Unknown" +) + +// A ConditionReason represents the reason a resource is in a condition. +// Should be only one word +type ConditionReason string + +// Some common Condition resons. +const ( + ReasonAvailable ConditionReason = "Available" + ReasonUnavailable ConditionReason = "Unavailable" + ReasonCreating ConditionReason = "Creating" + ReasonDeleting ConditionReason = "Deleting" + ReasonReconcileSuccess ConditionReason = "ReconcileSuccess" + ReasonReconcileError ConditionReason = "ReconcileError" + ReasonReconcilePaused ConditionReason = "ReconcilePaused" +) + +// A Condition that may apply to a resource. +type Condition struct { + // Type of this condition. At most one of each condition type may apply to + // a resource at any point in time. + Type ConditionType `json:"type"` + + // Status of this condition; is it currently True, False, or Unknown + Status corev1.ConditionStatus `json:"status"` + + // LastTransitionTime is the last time this condition transitioned from one + // status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime"` + + // LastSuccessfulTime is repository Last Successful Update Time + LastSuccessfulTime metav1.Time `json:"lastSuccessfulTime,omitempty"` + + // A Reason for this condition's last transition from one status to another. + Reason ConditionReason `json:"reason"` + + // A Message containing details about this condition's last transition from + // one status to another, if any. + // +optional + Message string `json:"message,omitempty"` +} + +// Equal returns true if the condition is identical to the supplied condition +func (c Condition) Equal(other Condition) bool { + return c.Type == other.Type && + c.Status == other.Status && + c.Reason == other.Reason && + c.Message == other.Message && + c.LastSuccessfulTime.Equal(&other.LastSuccessfulTime) && + c.LastTransitionTime.Equal(&other.LastTransitionTime) +} + +// WithMessage returns a condition by adding the provided message to existing +// condition. +func (c Condition) WithMessage(msg string) Condition { + c.Message = msg + return c +} + +// NOTE: Conditions are implemented as a slice rather than a map to comply +// with Kubernetes API conventions. Ideally we'd comply by using a map that +// marshaled to a JSON array, but doing so confuses the CRD schema generator. +// https://github.com/kubernetes/community/blob/9bf8cd/contributors/devel/sig-architecture/api-conventions.md#lists-of-named-subobjects-preferred-over-maps + +// NOTE: Do not manipulate Conditions directly. Use the Set method. + +// A ConditionedStatus reflects the observed status of a resource. Only +// one condition of each type may exist. +type ConditionedStatus struct { + // Conditions of the resource. + // +optional + Conditions []Condition `json:"conditions,omitempty"` +} + +// NewConditionedStatus returns a stat with the supplied conditions set. +func NewConditionedStatus(c ...Condition) *ConditionedStatus { + s := &ConditionedStatus{} + s.SetConditions(c...) + return s +} + +// GetCondition returns the condition for the given ConditionType if exists, +// otherwise returns nil +func (s *ConditionedStatus) GetCondition(ct ConditionType) Condition { + for _, c := range s.Conditions { + if c.Type == ct { + return c + } + } + + return Condition{Type: ct, Status: corev1.ConditionUnknown} +} + +// SetConditions sets the supplied conditions, replacing any existing conditions +// of the same type. This is a no-op if all supplied conditions are identical, +// ignoring the last transition time, to those already set. +func (s *ConditionedStatus) SetConditions(c ...Condition) { + for _, new := range c { + exists := false + for i, existing := range s.Conditions { + if existing.Type != new.Type { + continue + } + + if existing.Equal(new) { + exists = true + continue + } + + s.Conditions[i] = new + exists = true + } + if !exists { + s.Conditions = append(s.Conditions, new) + } + } +} + +// Equal returns true if the status is identical to the supplied status, +// ignoring the LastTransitionTimes and order of statuses. +func (s *ConditionedStatus) Equal(other *ConditionedStatus) bool { + if s == nil || other == nil { + return s == nil && other == nil + } + + if len(other.Conditions) != len(s.Conditions) { + return false + } + + sc := make([]Condition, len(s.Conditions)) + copy(sc, s.Conditions) + + oc := make([]Condition, len(other.Conditions)) + copy(oc, other.Conditions) + + // We should not have more than one condition of each type. + sort.Slice(sc, func(i, j int) bool { return sc[i].Type < sc[j].Type }) + sort.Slice(oc, func(i, j int) bool { return oc[i].Type < oc[j].Type }) + + for i := range sc { + if !sc[i].Equal(oc[i]) { + return false + } + } + + return true +} diff --git a/api/v1alpha1/llm.go b/api/v1alpha1/llm.go new file mode 100644 index 000000000..9ad46a67c --- /dev/null +++ b/api/v1alpha1/llm.go @@ -0,0 +1,41 @@ +/* +Copyright 2023 KubeAGI. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "errors" + + corev1 "k8s.io/api/core/v1" +) + +var ( + ErrMissingAPIKey = errors.New("missing apikey in auth info") +) + +type LLMType string + +const ( + OpenAI LLMType = "openai" +) + +func (o *AuthInfo) FromSecret(secret corev1.Secret) error { + o.APIKey = string(secret.Data["apiKey"]) + if o.APIKey == "" { + return ErrMissingAPIKey + } + return nil +} diff --git a/api/v1alpha1/llm_types.go b/api/v1alpha1/llm_types.go index 4dcc07014..1c599ca4a 100644 --- a/api/v1alpha1/llm_types.go +++ b/api/v1alpha1/llm_types.go @@ -20,22 +20,26 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - // LLMSpec defines the desired state of LLM type LLMSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + DisplayName string `json:"displayName,omitempty"` + // Type defines the type of llm + Type LLMType `json:"type"` + // URL keeps the URL of the llm service(Must required) + URL string `json:"url"` + // Auth keeps the authentication credentials when access llm + // keeps in k8s secret + Auth string `json:"auth,omitempty"` +} - // Foo is an example field of LLM. Edit llm_types.go to remove/update - Foo string `json:"foo,omitempty"` +type AuthInfo struct { + APIKey string `json:"apiKey,omitempty"` } // LLMStatus defines the observed state of LLM type LLMStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // ConditionedStatus is the current status + ConditionedStatus `json:",inline"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 3d92f466a..de25254b1 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -25,13 +25,67 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthInfo) DeepCopyInto(out *AuthInfo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthInfo. +func (in *AuthInfo) DeepCopy() *AuthInfo { + if in == nil { + return nil + } + out := new(AuthInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + in.LastSuccessfulTime.DeepCopyInto(&out.LastSuccessfulTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConditionedStatus) DeepCopyInto(out *ConditionedStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionedStatus. +func (in *ConditionedStatus) DeepCopy() *ConditionedStatus { + if in == nil { + return nil + } + out := new(ConditionedStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LLM) DeepCopyInto(out *LLM) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LLM. @@ -102,6 +156,7 @@ func (in *LLMSpec) DeepCopy() *LLMSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LLMStatus) DeepCopyInto(out *LLMStatus) { *out = *in + in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LLMStatus. diff --git a/charts/arcadia/Chart.yaml b/charts/arcadia/Chart.yaml index 522f39abe..be5e9bedf 100644 --- a/charts/arcadia/Chart.yaml +++ b/charts/arcadia/Chart.yaml @@ -1,24 +1,14 @@ apiVersion: v2 name: arcadia description: A Helm chart(KubeBB Component) for KubeAGI Arcadia - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. +version: 0.1.1 appVersion: "0.0.0" +keywords: + - kubeagi + - NativeAI +sources: + - https://github.com/kubeagi/arcadia +maintainers: + - name: bjwswang + url: https://github.com/bjwswang diff --git a/charts/arcadia/crds/arcadia.kubeagi.k8s.com.cn_laboratories.yaml b/charts/arcadia/crds/arcadia.kubeagi.k8s.com.cn_laboratories.yaml new file mode 100644 index 000000000..c45a1f152 --- /dev/null +++ b/charts/arcadia/crds/arcadia.kubeagi.k8s.com.cn_laboratories.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: laboratories.arcadia.kubeagi.k8s.com.cn +spec: + group: arcadia.kubeagi.k8s.com.cn + names: + kind: Laboratory + listKind: LaboratoryList + plural: laboratories + singular: laboratory + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Laboratory is the Schema for the laboratories 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: LaboratorySpec defines the desired state of Laboratory + properties: + foo: + description: Foo is an example field of Laboratory. Edit laboratory_types.go + to remove/update + type: string + type: object + status: + description: LaboratoryStatus defines the observed state of Laboratory + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/arcadia/crds/arcadia.kubeagi.k8s.com.cn_llms.yaml b/charts/arcadia/crds/arcadia.kubeagi.k8s.com.cn_llms.yaml new file mode 100644 index 000000000..e3477d1ac --- /dev/null +++ b/charts/arcadia/crds/arcadia.kubeagi.k8s.com.cn_llms.yaml @@ -0,0 +1,100 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: llms.arcadia.kubeagi.k8s.com.cn +spec: + group: arcadia.kubeagi.k8s.com.cn + names: + kind: LLM + listKind: LLMList + plural: llms + singular: llm + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: LLM is the Schema for the llms 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: LLMSpec defines the desired state of LLM + properties: + auth: + description: Auth keeps the authentication credentials when access + llm keeps in k8s secret + type: string + displayName: + type: string + type: + description: Type defines the type of llm + type: string + url: + description: URL keeps the URL of the llm service(Must required) + type: string + required: + - type + - url + type: object + status: + description: LLMStatus defines the observed state of LLM + properties: + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastSuccessfulTime: + description: LastSuccessfulTime is repository Last Successful + Update Time + format: date-time + type: string + lastTransitionTime: + description: LastTransitionTime is the last time this condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's + last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown + type: string + type: + description: Type of this condition. At most one of each condition + type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/arcadia/crds/arcadia.kubeagi.k8s.com.cn_prompts.yaml b/charts/arcadia/crds/arcadia.kubeagi.k8s.com.cn_prompts.yaml new file mode 100644 index 000000000..103ffa99f --- /dev/null +++ b/charts/arcadia/crds/arcadia.kubeagi.k8s.com.cn_prompts.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: prompts.arcadia.kubeagi.k8s.com.cn +spec: + group: arcadia.kubeagi.k8s.com.cn + names: + kind: Prompt + listKind: PromptList + plural: prompts + singular: prompt + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Prompt is the Schema for the prompts 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: PromptSpec defines the desired state of Prompt + properties: + foo: + description: Foo is an example field of Prompt. Edit prompt_types.go + to remove/update + type: string + type: object + status: + description: PromptStatus defines the observed state of Prompt + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/arcadia/templates/deployment.yaml b/charts/arcadia/templates/deployment.yaml new file mode 100644 index 000000000..cdfc12ce4 --- /dev/null +++ b/charts/arcadia/templates/deployment.yaml @@ -0,0 +1,55 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + control-plane: {{ .Release.Name }}-arcadia +spec: + selector: + matchLabels: + control-plane: {{ .Release.Name }}-arcadia + replicas: 1 + template: + metadata: + labels: + control-plane: {{ .Release.Name }}-arcadia + spec: + securityContext: + runAsNonRoot: true + # TODO(user): For common cases that do not require escalating privileges + # it is recommended to ensure that all your Pods/Containers are restrictive. + # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + # Please uncomment the following code if your project does NOT have to work on old Kubernetes + # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). + # seccompProfile: + # type: RuntimeDefault + containers: + - command: + - /manager + image: {{ .Values.deployment.image }} + imagePullPolicy: {{ .Values.deployment.imagePullPolicy }} + name: manager + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + # TODO(user): Configure the resources accordingly based on the project requirements. + # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: {{ toYaml .Values.deployment.resources | nindent 10 }} + serviceAccountName: {{ .Release.Name }} + terminationGracePeriodSeconds: 10 + diff --git a/charts/arcadia/templates/rbac.yaml b/charts/arcadia/templates/rbac.yaml new file mode 100644 index 000000000..419273522 --- /dev/null +++ b/charts/arcadia/templates/rbac.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Release.Name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} diff --git a/charts/arcadia/values.yaml b/charts/arcadia/values.yaml index aea0e10b2..601d3197b 100644 --- a/charts/arcadia/values.yaml +++ b/charts/arcadia/values.yaml @@ -1,3 +1,10 @@ -# Default values for arcadia. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. +deployment: + image: kubebb/core:v0.1.3 + imagePullPolcy: IfNotPresent + resources: + limits: + cpu: 5000m + memory: 1280Mi + requests: + cpu: 10m + memory: 64Mi diff --git a/config/crd/bases/arcadia.kubeagi.k8s.com.cn_llms.yaml b/config/crd/bases/arcadia.kubeagi.k8s.com.cn_llms.yaml index d37c8d220..e3477d1ac 100644 --- a/config/crd/bases/arcadia.kubeagi.k8s.com.cn_llms.yaml +++ b/config/crd/bases/arcadia.kubeagi.k8s.com.cn_llms.yaml @@ -35,13 +35,63 @@ spec: spec: description: LLMSpec defines the desired state of LLM properties: - foo: - description: Foo is an example field of LLM. Edit llm_types.go to - remove/update + auth: + description: Auth keeps the authentication credentials when access + llm keeps in k8s secret type: string + displayName: + type: string + type: + description: Type defines the type of llm + type: string + url: + description: URL keeps the URL of the llm service(Must required) + type: string + required: + - type + - url type: object status: description: LLMStatus defines the observed state of LLM + properties: + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastSuccessfulTime: + description: LastSuccessfulTime is repository Last Successful + Update Time + format: date-time + type: string + lastTransitionTime: + description: LastTransitionTime is the last time this condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's + last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown + type: string + type: + description: Type of this condition. At most one of each condition + type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array type: object type: object served: true diff --git a/go.mod b/go.mod index 4fee780f5..43820737a 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.18.1 + k8s.io/api v0.24.2 k8s.io/apimachinery v0.24.2 k8s.io/client-go v0.24.2 sigs.k8s.io/controller-runtime v0.12.2 @@ -71,7 +72,6 @@ require ( gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - k8s.io/api v0.24.2 // indirect k8s.io/apiextensions-apiserver v0.24.2 // indirect k8s.io/component-base v0.24.2 // indirect k8s.io/klog/v2 v2.60.1 // indirect