diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 0000000..776d6d5 --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,234 @@ +name: e2e +on: + issue_comment: + types: [created] + +env: + E2E_CHECK_NAME: e2e tests + +jobs: + triage: + runs-on: ubuntu-latest + name: Comment evaluate + outputs: + run-e2e: ${{ startsWith(github.event.comment.body,'/run-e2e') && steps.checkUserMember.outputs.isTeamMember == 'true' }} + is-debug: ${{ startsWith(github.event.comment.body,'/run-e2e debug') && steps.checkUserMember.outputs.isTeamMember == 'true' }} + pr_num: ${{ steps.parser.outputs.pr_num }} + image_tag: "pr-${{ steps.parser.outputs.pr_num }}-${{ steps.parser.outputs.commit_sha }}" + commit_sha: ${{ steps.parser.outputs.commit_sha }} + version_buildflags: ${{ steps.parser.outputs.version_buildflags }} + image_build_hash: ${{ steps.parser.outputs.image_build_hash }} + + steps: + - uses: actions/checkout@v3 + + - uses: tspascoal/get-user-teams-membership@v2 + id: checkUserMember + with: + username: ${{ github.actor }} + team: 'dev' + GITHUB_TOKEN: ${{ secrets.GH_CHECKING_USER_AUTH }} + + - name: Update comment with the execution url + if: ${{ startsWith(github.event.comment.body,'/run-e2e') && steps.checkUserMember.outputs.isTeamMember == 'true' }} + uses: peter-evans/create-or-update-comment@v2 + with: + comment-id: ${{ github.event.comment.id }} + body: | + **Update:** You can check the progress [here](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}) + reactions: rocket + + - name: Parse git info + if: ${{ startsWith(github.event.comment.body,'/run-e2e') && steps.checkUserMember.outputs.isTeamMember == 'true' }} + id: parser + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Get PR number + PR_URL="${{ github.event.issue.pull_request.url }}" + PR_NUM=${PR_URL##*/} + echo "Checking out from PR #$PR_NUM based on URL: $PR_URL" + echo "::set-output name=pr_num::$PR_NUM" + # Get commit SHA + git config --global --add safe.directory "$GITHUB_WORKSPACE" + gh pr checkout $PR_NUM + SHA=$(git log -n 1 --pretty=format:"%H") + echo "::set-output name=commit_sha::$SHA" + GIT_COMMIT=$(git describe --match=NeVeRmAtCh --tags --always --dirty | cut -c 1-7) + BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) + VERSION=$(git describe --tags `git rev-list --tags --max-count=1` | sed 's/v\(\)/\1/') + PKG=github.com/bentoml/yatai-image-builder + VERSION_BUILDFLAGS="-X '${PKG}/version.GitCommit=${GIT_COMMIT}' -X '${PKG}/version.Version=${VERSION}' -X '${PKG}/version.BuildDate=${BUILD_DATE}'" + echo "::set-output name=version_buildflags::$VERSION_BUILDFLAGS" + echo "::set-output name=image_build_hash::${{ hashFiles('Dockerfile', 'main.go', './apis/**', './controllers/**', './utils/**', './version/**', './yatai-client/**', '**/go.sum', '**go.mod') }}" + + build-test-images: + needs: triage + if: needs.triage.outputs.run-e2e == 'true' + runs-on: ubuntu-latest + steps: + - name: Set status in-progress + uses: LouisBrunner/checks-action@v1.5.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + sha: ${{ needs.triage.outputs.commit_sha }} + name: ${{ env.E2E_CHECK_NAME }} + status: in_progress + details_url: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} + + - uses: actions/checkout@v3 + + - name: Register workspace path + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Checkout Pull Request + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + id: checkout + run: | + gh pr checkout ${{ needs.triage.outputs.pr_num }} + + - name: Set up Docker Buildx + id: buildx + # Use the action from the master, as we've seen some inconsistencies with @v1 + # Issue: https://github.com/docker/build-push-action/issues/286 + uses: docker/setup-buildx-action@master + with: + install: true + + - name: Login to Quay.io + uses: docker/login-action@v1 + with: + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_ROBOT_TOKEN }} + + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + # Key is named differently to avoid collision + key: ${{ runner.os }}-multi-buildx-${{ needs.triage.outputs.image_build_hash }} + restore-keys: | + ${{ runner.os }}-multi-buildx + + - name: Build test image + uses: docker/build-push-action@v2 + with: + build-args: 'VERSION_BUILDFLAGS=${{ needs.triage.outputs.version_buildflags }}' + context: . + push: true + tags: quay.io/bentoml/test-yatai-image-builder:${{ needs.triage.outputs.image_tag }} + cache-from: type=local,src=/tmp/.buildx-cache + # Note the mode=max here + # More: https://github.com/moby/buildkit#--export-cache-options + # And: https://github.com/docker/buildx#--cache-tonametypetypekeyvalue + cache-to: type=local,mode=max,dest=/tmp/.buildx-cache-new + + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache + + run-test: + needs: [triage, build-test-images] + if: needs.triage.outputs.run-e2e == 'true' + runs-on: ubuntu-latest + steps: + - name: Set status in-progress + uses: LouisBrunner/checks-action@v1.5.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + sha: ${{ needs.triage.outputs.commit_sha }} + name: ${{ env.E2E_CHECK_NAME }} + status: in_progress + details_url: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} + + - name: Checkout + uses: actions/checkout@v3 + + - name: Register workspace path + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Checkout Pull Request + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + id: checkout + run: | + gh pr checkout ${{ needs.triage.outputs.pr_num }} + + - name: Install KinD + run: ./tests/gh-actions/install_kind.sh + + - name: Install Helm + run: ./tests/gh-actions/install_helm.sh + + - name: Create KinD Cluster + run: kind create cluster --config tests/gh-actions/kind-cluster-1-24.yaml + + - uses: oNaiPs/secrets-to-env-action@v1 + with: + secrets: ${{ toJSON(secrets) }} + + - name: Install yatai-image-builder + continue-on-error: true + id: install-yatai-image-builder + env: + YATAI_IMAGE_BUILDER_IMG_REPO: test-yatai-image-builder + YATAI_IMAGE_BUILDER_IMG_TAG: ${{ needs.triage.outputs.image_tag }} + run: | + ./tests/e2e/installation_test.sh + + - name: Setup upterm session + uses: lhotari/action-upterm@v1 + if: needs.triage.outputs.is-debug == 'true' + with: + ## limits ssh access and adds the ssh public key for the user which triggered the workflow + limit-access-to-actor: true + ## limits ssh access and adds the ssh public keys of the listed GitHub users + limit-access-to-users: yetone + + - name: Run e2e test + continue-on-error: true + id: test + if: steps.install-yatai-image-builder.outcome == 'success' + env: + DEBUG: ${{ needs.triage.outputs.is-debug }} + run: | + make test-e2e + + - name: Set status success + uses: LouisBrunner/checks-action@v1.5.0 + if: steps.test.outcome == 'success' && steps.install-yatai-image-builder.outcome == 'success' + with: + token: ${{ secrets.GITHUB_TOKEN }} + sha: ${{ needs.triage.outputs.commit_sha }} + name: ${{ env.E2E_CHECK_NAME }} + conclusion: success + details_url: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} + + - name: React to comment with success + uses: dkershner6/reaction-action@v1 + if: steps.test.outcome == 'success' && steps.install-yatai-image-builder.outcome == 'success' + with: + token: ${{ secrets.GITHUB_TOKEN }} + commentId: ${{ github.event.comment.id }} + reaction: "hooray" + + - name: React to comment with failure + uses: dkershner6/reaction-action@v1 + if: steps.test.outcome != 'success' || steps.install-yatai-image-builder.outcome != 'success' + with: + token: ${{ secrets.GITHUB_TOKEN }} + commentId: ${{ github.event.comment.id }} + reaction: "confused" + + - name: Set status failure + uses: LouisBrunner/checks-action@v1.5.0 + if: steps.test.outcome != 'success' || steps.install-yatai-image-builder.outcome != 'success' + with: + token: ${{ secrets.GITHUB_TOKEN }} + sha: ${{ needs.triage.outputs.commit_sha }} + name: ${{ env.E2E_CHECK_NAME }} + conclusion: failure + details_url: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 36a90bc..9a50f98 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -75,6 +75,10 @@ jobs: - name: Set up chart-testing uses: helm/chart-testing-action@v2.2.1 - - name: Render Helm Template + - name: Render yatai-image-builder-crds Helm Template + working-directory: ./helm/yatai-image-builder-crds + run: make template + + - name: Render yatai-image-builder Helm Template working-directory: ./helm/yatai-image-builder run: make template diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 76a70a4..42f7a00 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -67,7 +67,23 @@ jobs: echo ::set-output name=match::true fi - - name: Package, Index and Publish to public repo + - name: Package, Index and Publish yatai-image-builder-crds to public repo + working-directory: ./helm/yatai-image-builder-crds + if: steps.check-tag.outputs.match != 'true' + env: + VERSION: ${{ steps.tag.outputs.tag }} + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + run: make release + + - name: Package, Index and Publish yatai-image-builder-crds to devel repo + working-directory: ./helm/yatai-image-builder-crds + if: steps.check-tag.outputs.match == 'true' + env: + VERSION: ${{ steps.tag.outputs.tag }} + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + run: make release-devel + + - name: Package, Index and Publish yatai-image-builder to public repo working-directory: ./helm/yatai-image-builder if: steps.check-tag.outputs.match != 'true' env: @@ -75,7 +91,7 @@ jobs: API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} run: make release - - name: Package, Index and Publish to devel repo + - name: Package, Index and Publish yatai-image-builder to devel repo working-directory: ./helm/yatai-image-builder if: steps.check-tag.outputs.match == 'true' env: diff --git a/Dockerfile-kaniko b/Dockerfile-kaniko new file mode 100644 index 0000000..1934389 --- /dev/null +++ b/Dockerfile-kaniko @@ -0,0 +1 @@ +FROM gcr.io/kaniko-project/executor:debug diff --git a/Makefile b/Makefile index 0da5c63..f199524 100644 --- a/Makefile +++ b/Makefile @@ -95,6 +95,7 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(KUSTOMIZE) build config/crd > helm/yatai-image-builder-crds/templates/bentorequest.yaml .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. @@ -112,6 +113,10 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out +.PHONY: test-e2e # You will need to have a Kind cluster up in running to run this target +test-e2e: + go test ./tests/e2e/ -v -ginkgo.v -timeout 20m + ##@ Build .PHONY: build diff --git a/apis/resources/v1alpha1/bento_types.go b/apis/resources/v1alpha1/bento_types.go index 773c331..8ce3d81 100644 --- a/apis/resources/v1alpha1/bento_types.go +++ b/apis/resources/v1alpha1/bento_types.go @@ -55,7 +55,7 @@ type BentoSpec struct { Tag string `json:"tag"` // +kubebuilder:validation:Required Image string `json:"image"` - Context BentoContext `json:"context,omitempty"` + Context *BentoContext `json:"context,omitempty"` Runners []BentoRunner `json:"runners,omitempty"` ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` diff --git a/apis/resources/v1alpha1/bentorequest_types.go b/apis/resources/v1alpha1/bentorequest_types.go index 71843af..5096e62 100644 --- a/apis/resources/v1alpha1/bentorequest_types.go +++ b/apis/resources/v1alpha1/bentorequest_types.go @@ -33,17 +33,18 @@ const ( // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. type ExtraPodMetadata struct { - Annotations map[string]string `json:"annotations,omitempty"` - Labels map[string]string `json:"labels,omitempty"` + Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` + Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` } type ExtraPodSpec struct { - SchedulerName string `json:"schedulerName,omitempty"` - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - Affinity *corev1.Affinity `json:"affinity,omitempty"` - Tolerations []corev1.Toleration `json:"tolerations,omitempty"` - TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"` - ServiceAccountName string `json:"serviceAccountName,omitempty"` + PriorityClassName string `json:"priorityClassName,omitempty" yaml:"priorityClassName,omitempty"` + SchedulerName string `json:"schedulerName,omitempty" yaml:"schedulerName,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"` + Affinity *corev1.Affinity `json:"affinity,omitempty" yaml:"affinity,omitempty"` + Tolerations []corev1.Toleration `json:"tolerations,omitempty" yaml:"tolerations,omitempty"` + TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty" yaml:"topologySpreadConstraints,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty" yaml:"serviceAccountName,omitempty"` } // BentoRequestSpec defines the desired state of BentoRequest @@ -54,24 +55,30 @@ type BentoRequestSpec struct { // +kubebuilder:validation:Required BentoTag string `json:"bentoTag"` DownloadURL string `json:"downloadUrl,omitempty"` - Context BentoContext `json:"context,omitempty"` + Context *BentoContext `json:"context,omitempty"` Runners []BentoRunner `json:"runners,omitempty"` Models []BentoModel `json:"models,omitempty"` + // +kubebuilder:validation:Optional + Image string `json:"image,omitempty"` + ImageBuildTimeout *time.Duration `json:"imageBuildTimeout,omitempty"` // +kubebuilder:validation:Optional - ImageBuilderExtraPodMetadata ExtraPodMetadata `json:"imageBuilderExtraPodMetadata,omitempty"` + ImageBuilderExtraPodMetadata *ExtraPodMetadata `json:"imageBuilderExtraPodMetadata,omitempty"` // +kubebuilder:validation:Optional - ImageBuilderExtraPodSpec ExtraPodSpec `json:"imageBuilderExtraPodSpec,omitempty"` + ImageBuilderExtraPodSpec *ExtraPodSpec `json:"imageBuilderExtraPodSpec,omitempty"` // +kubebuilder:validation:Optional ImageBuilderExtraContainerEnv []corev1.EnvVar `json:"imageBuilderExtraContainerEnv,omitempty"` // +kubebuilder:validation:Optional - ImageBuilderContainerResources corev1.ResourceRequirements `json:"imageBuilderContainerResources,omitempty"` + ImageBuilderContainerResources *corev1.ResourceRequirements `json:"imageBuilderContainerResources,omitempty"` // +kubebuilder:validation:Optional DockerConfigJSONSecretName string `json:"dockerConfigJsonSecretName,omitempty"` + // +kubebuilder:validation:Optional + OCIRegistryInsecure *bool `json:"ociRegistryInsecure,omitempty"` + // +kubebuilder:validation:Optional DownloaderContainerEnvFrom []corev1.EnvFromSource `json:"downloaderContainerEnvFrom,omitempty"` } @@ -89,6 +96,7 @@ type BentoRequestStatus struct { //+kubebuilder:subresource:status //+kubebuilder:printcolumn:name="Bento-Tag",type="string",JSONPath=".spec.bentoTag",description="Bento Tag" //+kubebuilder:printcolumn:name="Download-Url",type="string",JSONPath=".spec.downloadUrl",description="Download URL" +//+kubebuilder:printcolumn:name="Image",type="string",JSONPath=".spec.image",description="Image" //+kubebuilder:printcolumn:name="Image-Exists",type="string",JSONPath=".status.conditions[?(@.type=='ImageExists')].status",description="Image Exists" //+kubebuilder:printcolumn:name="Bento-Available",type="string",JSONPath=".status.conditions[?(@.type=='BentoAvailable')].status",description="Bento Available" //+kubebuilder:printcolumn:name="Image-Builder-Pod-Phase",type="string",JSONPath=".status.imageBuilderPodStatus.phase",description="Image Builder Pod Phase" diff --git a/apis/resources/v1alpha1/zz_generated.deepcopy.go b/apis/resources/v1alpha1/zz_generated.deepcopy.go index 6f10ad1..4bfa9d7 100644 --- a/apis/resources/v1alpha1/zz_generated.deepcopy.go +++ b/apis/resources/v1alpha1/zz_generated.deepcopy.go @@ -195,7 +195,11 @@ func (in *BentoRequestList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BentoRequestSpec) DeepCopyInto(out *BentoRequestSpec) { *out = *in - out.Context = in.Context + if in.Context != nil { + in, out := &in.Context, &out.Context + *out = new(BentoContext) + **out = **in + } if in.Runners != nil { in, out := &in.Runners, &out.Runners *out = make([]BentoRunner, len(*in)) @@ -213,8 +217,16 @@ func (in *BentoRequestSpec) DeepCopyInto(out *BentoRequestSpec) { *out = new(timex.Duration) **out = **in } - in.ImageBuilderExtraPodMetadata.DeepCopyInto(&out.ImageBuilderExtraPodMetadata) - in.ImageBuilderExtraPodSpec.DeepCopyInto(&out.ImageBuilderExtraPodSpec) + if in.ImageBuilderExtraPodMetadata != nil { + in, out := &in.ImageBuilderExtraPodMetadata, &out.ImageBuilderExtraPodMetadata + *out = new(ExtraPodMetadata) + (*in).DeepCopyInto(*out) + } + if in.ImageBuilderExtraPodSpec != nil { + in, out := &in.ImageBuilderExtraPodSpec, &out.ImageBuilderExtraPodSpec + *out = new(ExtraPodSpec) + (*in).DeepCopyInto(*out) + } if in.ImageBuilderExtraContainerEnv != nil { in, out := &in.ImageBuilderExtraContainerEnv, &out.ImageBuilderExtraContainerEnv *out = make([]v1.EnvVar, len(*in)) @@ -222,7 +234,16 @@ func (in *BentoRequestSpec) DeepCopyInto(out *BentoRequestSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.ImageBuilderContainerResources.DeepCopyInto(&out.ImageBuilderContainerResources) + if in.ImageBuilderContainerResources != nil { + in, out := &in.ImageBuilderContainerResources, &out.ImageBuilderContainerResources + *out = new(v1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.OCIRegistryInsecure != nil { + in, out := &in.OCIRegistryInsecure, &out.OCIRegistryInsecure + *out = new(bool) + **out = **in + } if in.DownloaderContainerEnvFrom != nil { in, out := &in.DownloaderContainerEnvFrom, &out.DownloaderContainerEnvFrom *out = make([]v1.EnvFromSource, len(*in)) @@ -288,7 +309,11 @@ func (in *BentoRunner) DeepCopy() *BentoRunner { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BentoSpec) DeepCopyInto(out *BentoSpec) { *out = *in - out.Context = in.Context + if in.Context != nil { + in, out := &in.Context, &out.Context + *out = new(BentoContext) + **out = **in + } if in.Runners != nil { in, out := &in.Runners, &out.Runners *out = make([]BentoRunner, len(*in)) diff --git a/config/crd/bases/resources.yatai.ai_bentorequests.yaml b/config/crd/bases/resources.yatai.ai_bentorequests.yaml index 66f2508..9c1144f 100644 --- a/config/crd/bases/resources.yatai.ai_bentorequests.yaml +++ b/config/crd/bases/resources.yatai.ai_bentorequests.yaml @@ -24,6 +24,10 @@ spec: jsonPath: .spec.downloadUrl name: Download-Url type: string + - description: Image + jsonPath: .spec.image + name: Image + type: string - description: Image Exists jsonPath: .status.conditions[?(@.type=='ImageExists')].status name: Image-Exists @@ -104,6 +108,8 @@ spec: x-kubernetes-map-type: atomic type: object type: array + image: + type: string imageBuildTimeout: description: A Duration represents the elapsed time between two instants as an int64 nanosecond count. The representation limits the largest @@ -1129,6 +1135,8 @@ spec: additionalProperties: type: string type: object + priorityClassName: + type: string schedulerName: type: string serviceAccountName: @@ -1358,6 +1366,8 @@ spec: - tag type: object type: array + ociRegistryInsecure: + type: boolean runners: items: properties: diff --git a/controllers/resources/bentorequest_controller.go b/controllers/resources/bentorequest_controller.go index 0af73e3..757a440 100644 --- a/controllers/resources/bentorequest_controller.go +++ b/controllers/resources/bentorequest_controller.go @@ -18,16 +18,17 @@ package resources import ( "context" - // nolint: gosec - "crypto/md5" - "encoding/hex" + "strconv" + "fmt" "os" "reflect" "time" + "github.com/mitchellh/hashstructure/v2" "github.com/pkg/errors" - "gopkg.in/yaml.v3" + "github.com/rs/xid" + batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -35,11 +36,13 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" + "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/yaml" "bytes" "encoding/base64" @@ -71,6 +74,10 @@ import ( "github.com/aws/aws-sdk-go/service/ecr" ) +const ( + KubeAnnotationBentoRequestHash = "yatai.ai/bento-request-hash" +) + // BentoRequestReconciler reconciles a BentoRequest object type BentoRequestReconciler struct { client.Client @@ -163,17 +170,17 @@ func (r *BentoRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request } }() + var imageInfo ImageInfo + imageInfo, err = r.getImageInfo(ctx, GetImageInfoOption{ + BentoRequest: bentoRequest, + }) + if err != nil { + err = errors.Wrap(err, "get image info") + return + } + imageExistsCondition := meta.FindStatusCondition(bentoRequest.Status.Conditions, resourcesv1alpha1.BentoRequestConditionTypeImageExists) if imageExistsCondition == nil || imageExistsCondition.Status == metav1.ConditionUnknown { - var imageInfo ImageInfo - imageInfo, err = r.getImageInfo(ctx, GetImageInfoOption{ - BentoRequest: bentoRequest, - }) - if err != nil { - err = errors.Wrap(err, "get image info") - return - } - r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "CheckingImage", "Checking image exists: %s", imageInfo.ImageName) var imageExists bool imageExists, err = checkImageExists(imageInfo.DockerRegistry, imageInfo.InClusterImageName) @@ -195,7 +202,7 @@ func (r *BentoRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request Type: resourcesv1alpha1.BentoRequestConditionTypeImageExists, Status: metav1.ConditionTrue, Reason: "Reconciling", - Message: fmt.Sprintf("Image %s is already exists", imageInfo.ImageName), + Message: imageInfo.ImageName, }, ) if err != nil { @@ -241,62 +248,123 @@ func (r *BentoRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request return } - imageExists := imageExistsCondition != nil && imageExistsCondition.Status == metav1.ConditionTrue + imageExists := imageExistsCondition != nil && imageExistsCondition.Status == metav1.ConditionTrue && imageExistsCondition.Message == imageInfo.ImageName if !imageExists { - podName := strcase.ToKebab(fmt.Sprintf("yatai-bento-image-builder-%s", bentoRequest.Name)) - if len(podName) > 63 { - podName = fmt.Sprintf("yatai-bento-image-builder-%s", hash(bentoRequest.Name)) - if len(podName) > 63 { - podName = podName[:63] - } + var hashStr string + hashStr, err = r.getHashStr(bentoRequest) + if err != nil { + err = errors.Wrap(err, "get hash string") + return } - pod := &corev1.Pod{} - err = r.Get(ctx, types.NamespacedName{ - Name: podName, - Namespace: bentoRequest.Namespace, - }, pod) + jobLabels := map[string]string{ + commonconsts.KubeLabelBentoRequest: bentoRequest.Name, + } - podIsNotFound := k8serrors.IsNotFound(err) - if err != nil && !podIsNotFound { - err = errors.Wrapf(err, "get pod %s", podName) + jobs := &batchv1.JobList{} + err = r.List(ctx, jobs, client.InNamespace(req.Namespace), client.MatchingLabels(jobLabels)) + if err != nil { + err = errors.Wrap(err, "list jobs") return } - if podIsNotFound { - var imageInfo ImageInfo - imageInfo, err = r.getImageInfo(ctx, GetImageInfoOption{ - BentoRequest: bentoRequest, - }) - if err != nil { - err = errors.Wrap(err, "get image info") + reservedJobs := make([]*batchv1.Job, 0) + for _, job_ := range jobs.Items { + job_ := job_ + + oldHash := job_.Annotations[KubeAnnotationBentoRequestHash] + if oldHash != hashStr { + logs.Info("Because hash changed, delete old job", "job", job_.Name, "oldHash", oldHash, "newHash", hashStr) + // --cascade=foreground + err = r.Delete(ctx, &job_, &client.DeleteOptions{ + PropagationPolicy: &[]metav1.DeletionPropagation{metav1.DeletePropagationForeground}[0], + }) + if err != nil { + err = errors.Wrapf(err, "delete job %s", job_.Name) + return + } + result = ctrl.Result{ + Requeue: true, + } return + } else { + reservedJobs = append(reservedJobs, &job_) } + } + + var job *batchv1.Job + if len(reservedJobs) > 0 { + job = reservedJobs[0] + } + + if len(reservedJobs) > 1 { + for _, job_ := range reservedJobs[1:] { + logs.Info("Because has more than one job, delete old job", "job", job_.Name) + // --cascade=foreground + err = r.Delete(ctx, job_, &client.DeleteOptions{ + PropagationPolicy: &[]metav1.DeletionPropagation{metav1.DeletePropagationForeground}[0], + }) + if err != nil { + err = errors.Wrapf(err, "delete job %s", job_.Name) + return + } + } + } - r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Generating image builder pod: %s", podName) - pod, err = r.generateImageBuilderPod(ctx, GenerateImageBuilderPodOption{ - PodName: podName, - ImageInfo: imageInfo, - BentoRequest: bentoRequest, - RecreateIfFailed: true, + if job == nil { + job, err = r.generateImageBuilderJob(ctx, GenerateImageBuilderJobOption{ + ImageInfo: imageInfo, + BentoRequest: bentoRequest, }) if err != nil { - err = errors.Wrap(err, "create image builder pod") + err = errors.Wrap(err, "generate image builder job") return } - r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Creating image builder pod: %s", podName) - err = r.Create(ctx, pod) + r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderJob", "Creating image builder job: %s", job.Name) + err = r.Create(ctx, job) if err != nil { - err = errors.Wrapf(err, "create pod %s", podName) + err = errors.Wrapf(err, "create image builder job %s", job.Name) return } - r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Created image builder pod: %s", podName) + r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderJob", "Created image builder job: %s", job.Name) + result = ctrl.Result{ + RequeueAfter: 5 * time.Second, + } + return + } + + r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "CheckingImageBuilderJob", "Found image builder job: %s", job.Name) + + podLabels := map[string]string{ + "job-name": job.Name, + } + + pods := &corev1.PodList{} + err = r.List(ctx, pods, client.InNamespace(job.Namespace), client.MatchingLabels(podLabels)) + if err != nil { + err = errors.Wrap(err, "list pods") + return + } + + r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "CheckingImageBuilderJob", "Found %d pods for image builder job: %s/%s", len(pods.Items), job.Namespace, job.Name) + + var pod *corev1.Pod + for _, pod_ := range pods.Items { + pod_ := pod_ + pod = &pod_ + break + } + + if pod == nil { + r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "GetImageBuilderPod", "No image builder pod found, requeue") result = ctrl.Result{ RequeueAfter: 5 * time.Second, } return } + r.Recorder.Eventf(bentoRequest, corev1.EventTypeNormal, "GetImageBuilderPod", "Found image builder pod: %s", pod.Name) + /* Please don't blame me when you see this kind of code, this is to avoid "the object has been modified; please apply your changes to the latest version and try again" when updating CR status, @@ -407,7 +475,6 @@ func (r *BentoRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request if err != nil { return } - err = errors.Errorf("image builder pod %s status is %s", pod.Name, pod.Status.Phase) return } @@ -422,7 +489,7 @@ func (r *BentoRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request Type: resourcesv1alpha1.BentoRequestConditionTypeImageExists, Status: metav1.ConditionTrue, Reason: "Reconciling", - Message: fmt.Sprintf("Image builder pod %s status is %s", pod.Name, pod.Status.Phase), + Message: imageInfo.ImageName, }, ) if err != nil { @@ -438,15 +505,6 @@ func (r *BentoRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request return } - var imageInfo ImageInfo - imageInfo, err = r.getImageInfo(ctx, GetImageInfoOption{ - BentoRequest: bentoRequest, - }) - if err != nil { - err = errors.Wrap(err, "get image info") - return - } - bentoCR := &resourcesv1alpha1.Bento{ ObjectMeta: metav1.ObjectMeta{ Name: bentoRequest.Name, @@ -481,7 +539,7 @@ func (r *BentoRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request err = errors.Wrap(err, "get bento") return } - bentoCR.Spec.Context = resourcesv1alpha1.BentoContext{ + bentoCR.Spec.Context = &resourcesv1alpha1.BentoContext{ BentomlVersion: bento.Manifest.BentomlVersion, } bentoCR.Spec.Runners = make([]resourcesv1alpha1.BentoRunner, 0) @@ -611,11 +669,24 @@ func CheckECRImageExists(imageName string) (bool, error) { return false, nil } -func hash(text string) string { - // nolint: gosec - hasher := md5.New() - hasher.Write([]byte(text)) - return hex.EncodeToString(hasher.Sum(nil)) +type BentoImageBuildEngine string + +const ( + BentoImageBuildEngineKaniko BentoImageBuildEngine = "kaniko" + BentoImageBuildEngineBuildkit BentoImageBuildEngine = "buildkit" + BentoImageBuildEngineBuildkitRootless BentoImageBuildEngine = "buildkit-rootless" +) + +const ( + EnvBentoImageBuildEngine = "BENTO_IMAGE_BUILD_ENGINE" +) + +func getBentoImageBuildEngine() BentoImageBuildEngine { + engine := os.Getenv(EnvBentoImageBuildEngine) + if engine == "" { + return BentoImageBuildEngineKaniko + } + return BentoImageBuildEngine(engine) } func MakeSureDockerConfigJSONSecret(ctx context.Context, kubeCli *kubernetes.Clientset, namespace string, dockerRegistryConf *commonconfig.DockerRegistryConfig) (dockerConfigJSONSecret *corev1.Secret, err error) { @@ -725,7 +796,75 @@ func getYataiClient(ctx context.Context) (yataiClient **yataiclient.YataiClient, return } -func getDockerRegistry(ctx context.Context, cliset *kubernetes.Clientset) (dockerRegistry modelschemas.DockerRegistrySchema, err error) { +func getDockerRegistry(ctx context.Context, bentoRequest *resourcesv1alpha1.BentoRequest, cliset *kubernetes.Clientset) (dockerRegistry modelschemas.DockerRegistrySchema, err error) { + if bentoRequest != nil && bentoRequest.Spec.DockerConfigJSONSecretName != "" { + var secret *corev1.Secret + secret, err = cliset.CoreV1().Secrets(bentoRequest.Namespace).Get(ctx, bentoRequest.Spec.DockerConfigJSONSecretName, metav1.GetOptions{}) + if err != nil { + err = errors.Wrapf(err, "get docker config json secret %s", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + configJSON, ok := secret.Data[".dockerconfigjson"] + if !ok { + err = errors.Errorf("docker config json secret %s does not have .dockerconfigjson key", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + var configObj struct { + Auths map[string]struct { + Auth string `json:"auth"` + } `json:"auths"` + } + err = json.Unmarshal(configJSON, &configObj) + if err != nil { + err = errors.Wrapf(err, "unmarshal docker config json secret %s", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + imageRegistryURI, _, _ := xstrings.Partition(bentoRequest.Spec.Image, "/") + var server string + var auth string + if imageRegistryURI != "" { + for k, v := range configObj.Auths { + if k == imageRegistryURI { + server = k + auth = v.Auth + break + } + } + if server == "" { + for k, v := range configObj.Auths { + if strings.Contains(k, imageRegistryURI) { + server = k + auth = v.Auth + break + } + } + } + } + if server == "" { + for k, v := range configObj.Auths { + server = k + auth = v.Auth + break + } + } + if server == "" { + err = errors.Errorf("no auth in docker config json secret %s", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + dockerRegistry.Server = server + var credentials []byte + credentials, err = base64.StdEncoding.DecodeString(auth) + if err != nil { + err = errors.Wrapf(err, "cannot base64 decode auth in docker config json secret %s", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + dockerRegistry.Username, _, dockerRegistry.Password = xstrings.Partition(string(credentials), ":") + if bentoRequest.Spec.OCIRegistryInsecure != nil { + dockerRegistry.Secure = !*bentoRequest.Spec.OCIRegistryInsecure + } + return + } + dockerRegistryConfig, err := commonconfig.GetDockerRegistryConfig(ctx, cliset) if err != nil { err = errors.Wrap(err, "get docker registry") @@ -770,7 +909,10 @@ func getDockerRegistry(ctx context.Context, cliset *kubernetes.Clientset) (docke return } -func getBentoImageName(dockerRegistry modelschemas.DockerRegistrySchema, bentoRepositoryName, bentoVersion string, inCluster bool) string { +func getBentoImageName(bentoRequest *resourcesv1alpha1.BentoRequest, dockerRegistry modelschemas.DockerRegistrySchema, bentoRepositoryName, bentoVersion string, inCluster bool) string { + if bentoRequest != nil && bentoRequest.Spec.Image != "" { + return bentoRequest.Spec.Image + } var imageName string if inCluster { imageName = fmt.Sprintf("%s:yatai.%s.%s", dockerRegistry.BentosRepositoryURIInCluster, bentoRepositoryName, bentoVersion) @@ -837,18 +979,21 @@ func (r *BentoRequestReconciler) getImageInfo(ctx context.Context, opt GetImageI err = errors.Wrap(err, "create kubernetes clientset") return } - dockerRegistry, err := getDockerRegistry(ctx, kubeCli) + dockerRegistry, err := getDockerRegistry(ctx, opt.BentoRequest, kubeCli) if err != nil { err = errors.Wrap(err, "get docker registry") return } imageInfo.DockerRegistry = dockerRegistry - imageInfo.ImageName = getBentoImageName(dockerRegistry, bentoRepositoryName, bentoVersion, false) - imageInfo.InClusterImageName = getBentoImageName(dockerRegistry, bentoRepositoryName, bentoVersion, true) + imageInfo.ImageName = getBentoImageName(opt.BentoRequest, dockerRegistry, bentoRepositoryName, bentoVersion, false) + imageInfo.InClusterImageName = getBentoImageName(opt.BentoRequest, dockerRegistry, bentoRepositoryName, bentoVersion, true) imageInfo.DockerConfigJSONSecretName = opt.BentoRequest.Spec.DockerConfigJSONSecretName imageInfo.DockerRegistryInsecure = opt.BentoRequest.Annotations[commonconsts.KubeAnnotationDockerRegistryInsecure] == "true" + if opt.BentoRequest.Spec.OCIRegistryInsecure != nil { + imageInfo.DockerRegistryInsecure = *opt.BentoRequest.Spec.OCIRegistryInsecure + } if imageInfo.DockerConfigJSONSecretName == "" { var dockerRegistryConf *commonconfig.DockerRegistryConfig @@ -899,21 +1044,87 @@ func (r *BentoRequestReconciler) getBento(ctx context.Context, bentoRequest *res return } -type GenerateImageBuilderPodOption struct { - ImageInfo ImageInfo - PodName string - BentoRequest *resourcesv1alpha1.BentoRequest - RecreateIfFailed bool +func (r *BentoRequestReconciler) getImageBuilderJobName() string { + guid := xid.New() + return fmt.Sprintf("yatai-bento-image-builder-%s", guid.String()) } -func (r *BentoRequestReconciler) generateImageBuilderPod(ctx context.Context, opt GenerateImageBuilderPodOption) (pod *corev1.Pod, err error) { - bentoRepositoryName, _, bentoVersion := xstrings.Partition(opt.BentoRequest.Spec.BentoTag, ":") - kubeName := opt.PodName - kubeLabels := map[string]string{ +func (r *BentoRequestReconciler) getImageBuilderPodLabels(bentoRequest *resourcesv1alpha1.BentoRequest) map[string]string { + bentoRepositoryName, _, bentoVersion := xstrings.Partition(bentoRequest.Spec.BentoTag, ":") + return map[string]string{ + commonconsts.KubeLabelBentoRequest: bentoRequest.Name, commonconsts.KubeLabelIsBentoImageBuilder: "true", commonconsts.KubeLabelYataiBentoRepository: bentoRepositoryName, commonconsts.KubeLabelYataiBento: bentoVersion, } +} + +type GenerateImageBuilderJobOption struct { + ImageInfo ImageInfo + BentoRequest *resourcesv1alpha1.BentoRequest +} + +func (r *BentoRequestReconciler) generateImageBuilderJob(ctx context.Context, opt GenerateImageBuilderJobOption) (job *batchv1.Job, err error) { + // nolint: gosimple + podTemplateSpec, err := r.generateImageBuilderPodTemplateSpec(ctx, GenerateImageBuilderPodTemplateSpecOption{ + ImageInfo: opt.ImageInfo, + BentoRequest: opt.BentoRequest, + }) + if err != nil { + err = errors.Wrap(err, "generate image builder pod template spec") + return + } + kubeAnnotations := make(map[string]string) + hashStr, err := r.getHashStr(opt.BentoRequest) + if err != nil { + err = errors.Wrap(err, "failed to get hash string") + return + } + kubeAnnotations[KubeAnnotationBentoRequestHash] = hashStr + job = &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: r.getImageBuilderJobName(), + Namespace: opt.BentoRequest.Namespace, + Labels: r.getImageBuilderPodLabels(opt.BentoRequest), + Annotations: kubeAnnotations, + }, + Spec: batchv1.JobSpec{ + Completions: pointer.Int32Ptr(1), + Parallelism: pointer.Int32Ptr(1), + PodFailurePolicy: &batchv1.PodFailurePolicy{ + Rules: []batchv1.PodFailurePolicyRule{ + { + Action: batchv1.PodFailurePolicyActionFailJob, + OnExitCodes: &batchv1.PodFailurePolicyOnExitCodesRequirement{ + ContainerName: pointer.StringPtr(BuilderContainerName), + Operator: batchv1.PodFailurePolicyOnExitCodesOpIn, + Values: []int32{BuilderJobFailedExitCode}, + }, + }, + }, + }, + Template: *podTemplateSpec, + }, + } + err = ctrl.SetControllerReference(opt.BentoRequest, job, r.Scheme) + if err != nil { + err = errors.Wrapf(err, "set controller reference for job %s", job.Name) + return + } + return +} + +const BuilderContainerName = "builder" +const BuilderJobFailedExitCode = 42 + +type GenerateImageBuilderPodTemplateSpecOption struct { + ImageInfo ImageInfo + BentoRequest *resourcesv1alpha1.BentoRequest +} + +func (r *BentoRequestReconciler) generateImageBuilderPodTemplateSpec(ctx context.Context, opt GenerateImageBuilderPodTemplateSpecOption) (pod *corev1.PodTemplateSpec, err error) { + bentoRepositoryName, _, bentoVersion := xstrings.Partition(opt.BentoRequest.Spec.BentoTag, ":") + kubeLabels := r.getImageBuilderPodLabels(opt.BentoRequest) restConfig := clientconfig.GetConfigOrDie() kubeCli, err := kubernetes.NewForConfig(restConfig) if err != nil { @@ -923,9 +1134,6 @@ func (r *BentoRequestReconciler) generateImageBuilderPod(ctx context.Context, op inClusterImageName := opt.ImageInfo.InClusterImageName - logs := log.FromContext(ctx) - logs.Info(fmt.Sprintf("Generating image builder pod %s", kubeName)) - dockerConfigJSONSecretName := opt.ImageInfo.DockerConfigJSONSecretName dockerRegistryInsecure := opt.ImageInfo.DockerRegistryInsecure @@ -1008,7 +1216,7 @@ func (r *BentoRequestReconciler) generateImageBuilderPod(ctx context.Context, op } r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Got bento %s from yatai service", opt.BentoRequest.Spec.BentoTag) - if bento.TransmissionStrategy == modelschemas.TransmissionStrategyPresignedURL { + if bento.TransmissionStrategy != nil && *bento.TransmissionStrategy == modelschemas.TransmissionStrategyPresignedURL { var bento_ *schemasv1.BentoSchema r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Getting presigned url for bento %s from yatai service", opt.BentoRequest.Spec.BentoTag) bento_, err = yataiClient.PresignBentoDownloadURL(ctx, bentoRepositoryName, bentoVersion) @@ -1064,38 +1272,44 @@ func (r *BentoRequestReconciler) generateImageBuilderPod(ctx context.Context, op } // nolint: gosec - var awsAccessKeySecretName string - awsAccessKeyID := os.Getenv(commonconsts.EnvAWSAccessKeyID) - awsSecretAccessKey := os.Getenv(commonconsts.EnvAWSSecretAccessKey) - if awsAccessKeyID != "" && awsSecretAccessKey != "" { - // nolint: gosec - awsAccessKeySecretName = "yatai-image-builder-aws-access-key" - stringData := map[string]string{ - commonconsts.EnvAWSAccessKeyID: awsAccessKeyID, - commonconsts.EnvAWSSecretAccessKey: awsSecretAccessKey, - } - awsAccessKeySecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: awsAccessKeySecretName, - Namespace: opt.BentoRequest.Namespace, - }, - StringData: stringData, - } - r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Creating or updating secret %s in namespace %s", awsAccessKeySecretName, opt.BentoRequest.Namespace) - _, err = controllerutil.CreateOrUpdate(ctx, r.Client, awsAccessKeySecret, func() error { - awsAccessKeySecret.StringData = stringData - return nil - }) - if err != nil { - err = errors.Wrapf(err, "failed to create or update secret %s", awsAccessKeySecretName) - return + awsAccessKeySecretName := opt.BentoRequest.Labels[commonconsts.KubeLabelAWSAccessKeySecretName] + if awsAccessKeySecretName == "" { + awsAccessKeyID := os.Getenv(commonconsts.EnvAWSAccessKeyID) + awsSecretAccessKey := os.Getenv(commonconsts.EnvAWSSecretAccessKey) + if awsAccessKeyID != "" && awsSecretAccessKey != "" { + // nolint: gosec + awsAccessKeySecretName = "yatai-image-builder-aws-access-key" + stringData := map[string]string{ + commonconsts.EnvAWSAccessKeyID: awsAccessKeyID, + commonconsts.EnvAWSSecretAccessKey: awsSecretAccessKey, + } + awsAccessKeySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: awsAccessKeySecretName, + Namespace: opt.BentoRequest.Namespace, + }, + StringData: stringData, + } + r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Creating or updating secret %s in namespace %s", awsAccessKeySecretName, opt.BentoRequest.Namespace) + _, err = controllerutil.CreateOrUpdate(ctx, r.Client, awsAccessKeySecret, func() error { + awsAccessKeySecret.StringData = stringData + return nil + }) + if err != nil { + err = errors.Wrapf(err, "failed to create or update secret %s", awsAccessKeySecretName) + return + } + r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Secret %s is created or updated in namespace %s", awsAccessKeySecretName, opt.BentoRequest.Namespace) } - r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Secret %s is created or updated in namespace %s", awsAccessKeySecretName, opt.BentoRequest.Namespace) } internalImages := commonconfig.GetInternalImages() logrus.Infof("Image builder is using the images %v", *internalImages) + buildEngine := getBentoImageBuildEngine() + + privileged := buildEngine != BentoImageBuildEngineBuildkitRootless + bentoDownloadCommandTemplate, err := template.New("downloadCommand").Parse(` set -e @@ -1113,6 +1327,10 @@ echo "Extracting bento tar file..." tar -xvf /tmp/downloaded.tar echo "Removing bento tar file..." rm /tmp/downloaded.tar +{{if not .Privileged}} +echo "Changing directory permission..." +chown -R 1000:1000 /workspace +{{end}} echo "Done" `) @@ -1128,6 +1346,7 @@ echo "Done" "BentoDownloadHeader": bentoDownloadHeader, "BentoRepositoryName": bentoRepositoryName, "BentoVersion": bentoVersion, + "Privileged": privileged, }) if err != nil { err = errors.Wrap(err, "failed to execute download command template") @@ -1139,11 +1358,11 @@ echo "Done" downloaderContainerResources := corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("1000m"), - corev1.ResourceMemory: resource.MustParse("1000Mi"), + corev1.ResourceMemory: resource.MustParse("3000Mi"), }, Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("100m"), - corev1.ResourceMemory: resource.MustParse("100Mi"), + corev1.ResourceMemory: resource.MustParse("1000Mi"), }, } @@ -1181,6 +1400,12 @@ echo "Done" VolumeMounts: volumeMounts, Resources: downloaderContainerResources, EnvFrom: downloaderContainerEnvFrom, + Env: []corev1.EnvVar{ + { + Name: "AWS_EC2_METADATA_DISABLED", + Value: "true", + }, + }, }, } @@ -1235,7 +1460,7 @@ echo "Done" } r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Model %s is got from yatai service", model.Tag) - if model_.TransmissionStrategy == modelschemas.TransmissionStrategyPresignedURL { + if model_.TransmissionStrategy != nil && *model_.TransmissionStrategy == modelschemas.TransmissionStrategyPresignedURL { var model0 *schemasv1.ModelSchema r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Getting presigned url for model %s from yatai service", model.Tag) model0, err = yataiClient.PresignModelDownloadURL(ctx, modelRepositoryName, modelVersion) @@ -1271,6 +1496,10 @@ tar -xvf /tmp/downloaded.tar echo -n '{{.ModelVersion}}' > {{.ModelRepositoryDirPath}}/latest echo "Removing model tar file..." rm /tmp/downloaded.tar +{{if not .Privileged}} +echo "Changing directory permission..." +chown -R 1000:1000 /workspace +{{end}} echo "Done" `)).Execute(&modelDownloadCommandOutput, map[string]interface{}{ "ModelDirPath": modelDirPath, @@ -1279,6 +1508,7 @@ echo "Done" "ModelRepositoryDirPath": modelRepositoryDirPath, "ModelRepositoryName": modelRepositoryName, "ModelVersion": modelVersion, + "Privileged": privileged, }) if err != nil { err = errors.Wrap(err, "failed to generate download command") @@ -1296,12 +1526,19 @@ echo "Done" VolumeMounts: volumeMounts, Resources: downloaderContainerResources, EnvFrom: downloaderContainerEnvFrom, + Env: []corev1.EnvVar{ + { + Name: "AWS_EC2_METADATA_DISABLED", + Value: "true", + }, + }, }) } var globalExtraPodMetadata *resourcesv1alpha1.ExtraPodMetadata var globalExtraPodSpec *resourcesv1alpha1.ExtraPodSpec var globalExtraContainerEnv []corev1.EnvVar + var globalDefaultImageBuilderContainerResources *corev1.ResourceRequirements var buildArgs []string var builderArgs []string @@ -1323,6 +1560,7 @@ echo "Done" if !configCmIsNotFound { r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Configmap %s is got from namespace %s", configCmName, configNamespace) + globalExtraPodMetadata = &resourcesv1alpha1.ExtraPodMetadata{} if val, ok := configCm.Data["extra_pod_metadata"]; ok { @@ -1353,6 +1591,15 @@ echo "Done" } } + if val, ok := configCm.Data["default_image_builder_container_resources"]; ok { + globalDefaultImageBuilderContainerResources = &corev1.ResourceRequirements{} + err = yaml.Unmarshal([]byte(val), globalDefaultImageBuilderContainerResources) + if err != nil { + err = errors.Wrapf(err, "failed to yaml unmarshal default_image_builder_container_resources, please check the configmap %s in namespace %s", configCmName, configNamespace) + return + } + } + buildArgs = []string{} if val, ok := configCm.Data["build_args"]; ok { @@ -1399,18 +1646,80 @@ echo "Done" }) } - var command []string + if !privileged { + envs = append(envs, corev1.EnvVar{ + Name: "BUILDKITD_FLAGS", + Value: "--oci-worker-no-process-sandbox", + }) + } + + kubeAnnotations := make(map[string]string) + command := []string{ + "/kaniko/executor", + } args := []string{ "--context=/workspace/buildcontext", "--verbosity=info", + "--cache=true", + "--compressed-caching=false", fmt.Sprintf("--dockerfile=%s", dockerFilePath), fmt.Sprintf("--insecure=%v", dockerRegistryInsecure), fmt.Sprintf("--destination=%s", inClusterImageName), } + var builderImage string + switch buildEngine { + case BentoImageBuildEngineKaniko: + builderImage = internalImages.Kaniko + case BentoImageBuildEngineBuildkit: + builderImage = internalImages.Buildkit + case BentoImageBuildEngineBuildkitRootless: + builderImage = internalImages.BuildkitRootless + default: + err = errors.Errorf("unknown bento image build engine %s", buildEngine) + return + } + + isBuildkit := buildEngine == BentoImageBuildEngineBuildkit || buildEngine == BentoImageBuildEngineBuildkitRootless + + if isBuildkit { + command = []string{"buildctl-daemonless.sh"} + args = []string{ + "build", + "--frontend", + "dockerfile.v0", + "--local", + "context=/workspace/buildcontext", + "--local", + fmt.Sprintf("dockerfile=%s", filepath.Dir(dockerFilePath)), + "--output", + fmt.Sprintf("type=image,name=%s,push=true,registry.insecure=%v", inClusterImageName, dockerRegistryInsecure), + } + } + + var builderContainerSecurityContext *corev1.SecurityContext + + if buildEngine == BentoImageBuildEngineBuildkit { + builderContainerSecurityContext = &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + } + } else if buildEngine == BentoImageBuildEngineBuildkitRootless { + kubeAnnotations["container.apparmor.security.beta.kubernetes.io/builder"] = "unconfined" + builderContainerSecurityContext = &corev1.SecurityContext{ + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeUnconfined, + }, + RunAsUser: pointer.Int64Ptr(1000), + RunAsGroup: pointer.Int64Ptr(1000), + } + } // add build args to pass via --build-arg for _, buildArg := range buildArgs { - args = append(args, fmt.Sprintf("--build-arg=%s", buildArg)) + if isBuildkit { + args = append(args, "--opt", fmt.Sprintf("build-arg:%s", buildArg)) + } else { + args = append(args, fmt.Sprintf("--build-arg=%s", buildArg)) + } } // add other arguments to builder args = append(args, builderArgs...) @@ -1419,7 +1728,8 @@ echo "Done" // nolint: gosec buildArgsSecretName := "yatai-image-builder-build-args" r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Getting secret %s from namespace %s", buildArgsSecretName, configNamespace) - buildArgsSecret, err := kubeCli.CoreV1().Secrets(configNamespace).Get(ctx, buildArgsSecretName, metav1.GetOptions{}) + buildArgsSecret := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: buildArgsSecretName, Namespace: configNamespace}, buildArgsSecret) buildArgsSecretIsNotFound := k8serrors.IsNotFound(err) if err != nil && !buildArgsSecretIsNotFound { err = errors.Wrap(err, "failed to get secret") @@ -1479,37 +1789,63 @@ echo "Done" }, }) - args = append(args, fmt.Sprintf("--build-arg=%s=$(%s)", key, envName)) + if isBuildkit { + args = append(args, "--opt", fmt.Sprintf("build-arg:%s=$(%s)", key, envName)) + } else { + args = append(args, fmt.Sprintf("--build-arg=%s=$(%s)", key, envName)) + } } } else { r.Recorder.Eventf(opt.BentoRequest, corev1.EventTypeNormal, "GenerateImageBuilderPod", "Secret %s is not found in namespace %s", buildArgsSecretName, configNamespace) } - builderImage := internalImages.Kaniko + builderContainerArgs := []string{ + "-c", + fmt.Sprintf("%s %s && exit 0 || exit %d", strings.Join(command, " "), strings.Join(args, " "), BuilderJobFailedExitCode), + } + + container := corev1.Container{ + Name: BuilderContainerName, + Image: builderImage, + ImagePullPolicy: corev1.PullAlways, + Command: []string{"sh"}, + Args: builderContainerArgs, + VolumeMounts: volumeMounts, + Env: envs, + TTY: true, + Stdin: true, + SecurityContext: builderContainerSecurityContext, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("3Gi"), + }, + }, + } + + if globalDefaultImageBuilderContainerResources != nil { + container.Resources = *globalDefaultImageBuilderContainerResources + } + + if opt.BentoRequest.Spec.ImageBuilderContainerResources != nil { + container.Resources = *opt.BentoRequest.Spec.ImageBuilderContainerResources + } - pod = &corev1.Pod{ + pod = &corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Name: kubeName, - Namespace: opt.BentoRequest.Namespace, - Labels: kubeLabels, + Labels: kubeLabels, + Annotations: kubeAnnotations, }, Spec: corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, Volumes: volumes, InitContainers: initContainers, Containers: []corev1.Container{ - { - Name: "builder", - Image: builderImage, - ImagePullPolicy: corev1.PullAlways, - Command: command, - Args: args, - VolumeMounts: volumeMounts, - Env: envs, - TTY: true, - Stdin: true, - Resources: opt.BentoRequest.Spec.ImageBuilderContainerResources, - }, + container, }, }, } @@ -1524,15 +1860,18 @@ echo "Done" } } - for k, v := range opt.BentoRequest.Spec.ImageBuilderExtraPodMetadata.Annotations { - pod.Annotations[k] = v - } + if opt.BentoRequest.Spec.ImageBuilderExtraPodMetadata != nil { + for k, v := range opt.BentoRequest.Spec.ImageBuilderExtraPodMetadata.Annotations { + pod.Annotations[k] = v + } - for k, v := range opt.BentoRequest.Spec.ImageBuilderExtraPodMetadata.Labels { - pod.Labels[k] = v + for k, v := range opt.BentoRequest.Spec.ImageBuilderExtraPodMetadata.Labels { + pod.Labels[k] = v + } } if globalExtraPodSpec != nil { + pod.Spec.PriorityClassName = globalExtraPodSpec.PriorityClassName pod.Spec.SchedulerName = globalExtraPodSpec.SchedulerName pod.Spec.NodeSelector = globalExtraPodSpec.NodeSelector pod.Spec.Affinity = globalExtraPodSpec.Affinity @@ -1541,28 +1880,34 @@ echo "Done" pod.Spec.ServiceAccountName = globalExtraPodSpec.ServiceAccountName } - if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.SchedulerName != "" { - pod.Spec.SchedulerName = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.SchedulerName - } + if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec != nil { + if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.PriorityClassName != "" { + pod.Spec.PriorityClassName = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.PriorityClassName + } - if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.NodeSelector != nil { - pod.Spec.NodeSelector = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.NodeSelector - } + if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.SchedulerName != "" { + pod.Spec.SchedulerName = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.SchedulerName + } - if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.Affinity != nil { - pod.Spec.Affinity = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.Affinity - } + if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.NodeSelector != nil { + pod.Spec.NodeSelector = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.NodeSelector + } - if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.Tolerations != nil { - pod.Spec.Tolerations = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.Tolerations - } + if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.Affinity != nil { + pod.Spec.Affinity = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.Affinity + } - if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.TopologySpreadConstraints != nil { - pod.Spec.TopologySpreadConstraints = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.TopologySpreadConstraints - } + if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.Tolerations != nil { + pod.Spec.Tolerations = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.Tolerations + } - if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.ServiceAccountName != "" { - pod.Spec.ServiceAccountName = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.ServiceAccountName + if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.TopologySpreadConstraints != nil { + pod.Spec.TopologySpreadConstraints = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.TopologySpreadConstraints + } + + if opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.ServiceAccountName != "" { + pod.Spec.ServiceAccountName = opt.BentoRequest.Spec.ImageBuilderExtraPodSpec.ServiceAccountName + } } if pod.Spec.ServiceAccountName == "" { @@ -1598,13 +1943,26 @@ echo "Done" pod.Spec.Containers[i].Env = env } - err = ctrl.SetControllerReference(opt.BentoRequest, pod, r.Scheme) + return +} + +func (r *BentoRequestReconciler) getHashStr(bentoRequest *resourcesv1alpha1.BentoRequest) (string, error) { + var hash uint64 + hash, err := hashstructure.Hash(struct { + Spec resourcesv1alpha1.BentoRequestSpec + Labels map[string]string + Annotations map[string]string + }{ + Spec: bentoRequest.Spec, + Labels: bentoRequest.Labels, + Annotations: bentoRequest.Annotations, + }, hashstructure.FormatV2, nil) if err != nil { - err = errors.Wrapf(err, "set controller reference for pod %s", pod.Name) - return + err = errors.Wrap(err, "get bentoRequest CR spec hash") + return "", err } - - return + hashStr := strconv.FormatUint(hash, 10) + return hashStr, nil } func (r *BentoRequestReconciler) doRegisterYataiComponent() (err error) { @@ -1635,9 +1993,9 @@ func (r *BentoRequestReconciler) doRegisterYataiComponent() (err error) { return } - namespace, err := commonconfig.GetYataiDeploymentNamespace(ctx, cliset) + namespace, err := commonconfig.GetYataiImageBuilderNamespace(ctx, cliset) if err != nil { - err = errors.Wrap(err, "get yatai deployment namespace") + err = errors.Wrap(err, "get yatai image builder namespace") return } @@ -1694,7 +2052,7 @@ func (r *BentoRequestReconciler) SetupWithManager(mgr ctrl.Manager) error { err := ctrl.NewControllerManagedBy(mgr). For(&resourcesv1alpha1.BentoRequest{}). Owns(&resourcesv1alpha1.Bento{}). - Owns(&corev1.Pod{}). + Owns(&batchv1.Job{}). WithEventFilter(pred). Complete(r) return errors.Wrap(err, "failed to setup BentoRequest controller") diff --git a/go.mod b/go.mod index d047930..fc1ca69 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,16 @@ go 1.18 require ( github.com/aws/aws-sdk-go v1.44.152 - github.com/bentoml/yatai-common v0.0.0-20221204065908-975ef2e3714e - github.com/bentoml/yatai-schemas v0.0.0-20221123041958-d3ff9b721451 + github.com/bentoml/yatai-common v0.0.0-20230817022739-8e5bce48245a + github.com/bentoml/yatai-schemas v0.0.0-20230418023541-71c74442a90f github.com/huandu/xstrings v1.3.2 github.com/iancoleman/strcase v0.2.0 + github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.19.0 + github.com/onsi/ginkgo/v2 v2.3.1 + github.com/onsi/gomega v1.22.1 github.com/pkg/errors v0.9.1 + github.com/rs/xid v1.5.0 github.com/sirupsen/logrus v1.8.1 k8s.io/api v0.25.0 k8s.io/apimachinery v0.25.0 @@ -20,6 +23,7 @@ require ( ) require ( + github.com/bentoml/yatai-deployment v1.1.10 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -55,7 +59,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/gnostic v0.6.8 // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/imdario/mergo v0.3.12 // indirect @@ -77,28 +81,28 @@ require ( go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.21.0 // indirect golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.1.0 // indirect - golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/tools v0.4.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.25.0 // indirect k8s.io/component-base v0.25.0 // indirect k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect k8s.io/klog/v2 v2.70.1 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + sigs.k8s.io/yaml v1.3.0 ) diff --git a/go.sum b/go.sum index c66fd75..bbb142d 100644 --- a/go.sum +++ b/go.sum @@ -77,10 +77,12 @@ github.com/aws/aws-sdk-go v1.44.152 h1:L9aaepO8wHB67gwuGD8VgIYH/cmQDxieCt7FeLa0+ github.com/aws/aws-sdk-go v1.44.152/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/bentoml/yatai-common v0.0.0-20221204065908-975ef2e3714e h1:et8vRFRG2CgnFUElqpXVrerB1DkV2DcpmFWpzAGAvqo= -github.com/bentoml/yatai-common v0.0.0-20221204065908-975ef2e3714e/go.mod h1:pox0XYk/bVUwKkadn0XwWHEbJmxSEeN3+HwGA4a8uOQ= -github.com/bentoml/yatai-schemas v0.0.0-20221123041958-d3ff9b721451 h1:FNxCbN61Ev8ea6BXzlfmRUT5CYNmqlOv8zDRGs8ufVE= -github.com/bentoml/yatai-schemas v0.0.0-20221123041958-d3ff9b721451/go.mod h1:q7tt064G8YIiAwQabKyVaKEdSIHYDQA9Oyt+kyCsflU= +github.com/bentoml/yatai-common v0.0.0-20230817022739-8e5bce48245a h1:7y2Vhx+4rFv8gHzba0RT5luLsrC3MylFC6avCOLhKsg= +github.com/bentoml/yatai-common v0.0.0-20230817022739-8e5bce48245a/go.mod h1:SNmX+4Rn8F6POYKtxXyS7EdbYQ9IjFK6gAmRMu7CnC4= +github.com/bentoml/yatai-deployment v1.1.10 h1:YADdLkwyUcH4Zsyp/O4ymJdqNoMFIu/KKQrfnhNsRd0= +github.com/bentoml/yatai-deployment v1.1.10/go.mod h1:j73glvFeo11oP5I5J7lOxyY5gApS199qFxA1NzplX+Y= +github.com/bentoml/yatai-schemas v0.0.0-20230418023541-71c74442a90f h1:G/yhoMaNIWrc4r9Sp3/wjAlLwAq4jWHzsNQ97pJ53KM= +github.com/bentoml/yatai-schemas v0.0.0-20230418023541-71c74442a90f/go.mod h1:5pwTMTCo03tajWafBo3YmJf+gLmz7sxnA/g+AzueZvM= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -215,8 +217,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -297,6 +299,8 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= 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= @@ -316,11 +320,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= +github.com/onsi/ginkgo/v2 v2.3.1 h1:8SbseP7qM32WcvE6VaN6vfXxv698izmsJ1UQX9ve7T8= +github.com/onsi/ginkgo/v2 v2.3.1/go.mod h1:Sv4yQXwG5VmF7tm3Q5Z+RWUpPo24LF1mpnz2crUb8Ys= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI= +github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= @@ -360,6 +365,8 @@ github.com/prune998/docker-registry-client v0.0.0-20200114164314-f8cd511a014c/go github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -453,8 +460,9 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -501,8 +509,9 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -519,8 +528,8 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -533,6 +542,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -599,12 +609,14 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -614,8 +626,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -677,8 +690,9 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -816,8 +830,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/helm/yatai-image-builder-crds/.helmignore b/helm/yatai-image-builder-crds/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm/yatai-image-builder-crds/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/yatai-image-builder-crds/Chart.yaml b/helm/yatai-image-builder-crds/Chart.yaml new file mode 100644 index 0000000..c710505 --- /dev/null +++ b/helm/yatai-image-builder-crds/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: yatai-image-builder-crds +description: A Helm chart for Kubernetes + +# 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. +appVersion: "1.16.0" diff --git a/helm/yatai-image-builder-crds/Makefile b/helm/yatai-image-builder-crds/Makefile new file mode 100644 index 0000000..295610d --- /dev/null +++ b/helm/yatai-image-builder-crds/Makefile @@ -0,0 +1,44 @@ +.DEFAULT_GOAL := help + +ifndef VERSION + VERSION := "0.0.1-dummy.1" +endif + +ifndef CLONE_DIR + CLONE_DIR := $(shell mktemp -d) +endif + +help: ## Show all Makefile targets + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +lint: + ct lint --all + +cleanup: + rm yatai-image-builder-crds-*.tgz || true + +release: cleanup + helm package . --version ${VERSION} --app-version ${VERSION} + if [ ${GITHUB_ACTIONS} ]; then git config --global user.name "github-actions[bot]"; fi + if [ ${GITHUB_ACTIONS} ]; then git config --global user.email "github-actions[bot]@users.noreply.github.com"; fi + if [ ${GITHUB_ACTIONS} ]; then git config --global credential.helper store; fi + git clone --single-branch --branch gh-pages "https://x-access-token:${API_TOKEN_GITHUB}@github.com/bentoml/helm-charts.git" "${CLONE_DIR}" + cp yatai-image-builder-crds-${VERSION}.tgz ${CLONE_DIR}/packages/ + cd ${CLONE_DIR}; helm repo index . + cd ${CLONE_DIR}; git add . && git commit --message "release yatai-image-builder-crds ${VERSION}" && git push -f origin HEAD:gh-pages || exit 1 + rm -rf ${CLONE_DIR} || true + +release-devel: cleanup + helm package . --version ${VERSION} --app-version ${VERSION} + if [ ${GITHUB_ACTIONS} ]; then git config --global user.name "github-actions[bot]"; fi + if [ ${GITHUB_ACTIONS} ]; then git config --global user.email "github-actions[bot]@users.noreply.github.com"; fi + if [ ${GITHUB_ACTIONS} ]; then git config --global credential.helper store; fi + git clone --single-branch --branch gh-pages "https://x-access-token:${API_TOKEN_GITHUB}@github.com/bentoml/helm-charts-devel.git" "${CLONE_DIR}" + cp yatai-image-builder-crds-${VERSION}.tgz ${CLONE_DIR}/packages/ + cd ${CLONE_DIR}; helm repo index . + cd ${CLONE_DIR}; git add . && git commit --message "release yatai-image-builder-crds ${VERSION}" && git push -f origin HEAD:gh-pages || exit 1 + rm -rf ${CLONE_DIR} || true + +template: + helm template yatai-image-builder-crds ./ -n yatai-image-builder-crds --debug + diff --git a/helm/yatai-image-builder-crds/templates/NOTES.txt b/helm/yatai-image-builder-crds/templates/NOTES.txt new file mode 100644 index 0000000..4bdb803 --- /dev/null +++ b/helm/yatai-image-builder-crds/templates/NOTES.txt @@ -0,0 +1,4 @@ +Waiting for yatai-image-builder CRDs to be established: + + kubectl wait --for condition=established --timeout=120s crd/bentorequests.resources.yatai.ai + kubectl wait --for condition=established --timeout=120s crd/bentoes.resources.yatai.ai diff --git a/helm/yatai-image-builder-crds/templates/_helpers.tpl b/helm/yatai-image-builder-crds/templates/_helpers.tpl new file mode 100644 index 0000000..7f81d4d --- /dev/null +++ b/helm/yatai-image-builder-crds/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "yatai-image-builder-crds.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "yatai-image-builder-crds.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "yatai-image-builder-crds.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "yatai-image-builder-crds.labels" -}} +helm.sh/chart: {{ include "yatai-image-builder-crds.chart" . }} +{{ include "yatai-image-builder-crds.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "yatai-image-builder-crds.selectorLabels" -}} +app.kubernetes.io/name: {{ include "yatai-image-builder-crds.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "yatai-image-builder-crds.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "yatai-image-builder-crds.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm/yatai-image-builder/crds/bentorequest.yaml b/helm/yatai-image-builder-crds/templates/bentorequest.yaml similarity index 52% rename from helm/yatai-image-builder/crds/bentorequest.yaml rename to helm/yatai-image-builder-crds/templates/bentorequest.yaml index f666586..ad44600 100644 --- a/helm/yatai-image-builder/crds/bentorequest.yaml +++ b/helm/yatai-image-builder-crds/templates/bentorequest.yaml @@ -32,14 +32,10 @@ spec: description: Bento is the Schema for the bentoes API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -55,12 +51,10 @@ spec: type: string imagePullSecrets: items: - description: LocalObjectReference contains enough information to - let you locate the referenced object inside the same namespace. + description: LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace. properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' type: string type: object x-kubernetes-map-type: atomic @@ -90,9 +84,7 @@ spec: description: BentoStatus defines the observed state of Bento properties: ready: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: boolean required: - ready @@ -128,6 +120,10 @@ spec: jsonPath: .spec.downloadUrl name: Download-Url type: string + - description: Image + jsonPath: .spec.image + name: Image + type: string - description: Image Exists jsonPath: .status.conditions[?(@.type=='ImageExists')].status name: Image-Exists @@ -149,14 +145,10 @@ spec: description: BentoRequest is the Schema for the bentorequests API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -182,8 +174,7 @@ spec: description: The ConfigMap to select from properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' type: string optional: description: Specify whether the ConfigMap must be defined @@ -191,15 +182,13 @@ spec: type: object x-kubernetes-map-type: atomic prefix: - description: An optional identifier to prepend to each key in - the ConfigMap. Must be a C_IDENTIFIER. + description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. type: string secretRef: description: The Secret to select from properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' type: string optional: description: Specify whether the Secret must be defined @@ -208,10 +197,10 @@ spec: x-kubernetes-map-type: atomic type: object type: array + image: + type: string imageBuildTimeout: - description: A Duration represents the elapsed time between two instants - as an int64 nanosecond count. The representation limits the largest - representable duration to approximately 290 years. + description: A Duration represents the elapsed time between two instants as an int64 nanosecond count. The representation limits the largest representable duration to approximately 290 years. format: int64 type: integer imageBuilderContainerResources: @@ -224,8 +213,7 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources - allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object requests: additionalProperties: @@ -234,34 +222,21 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object imageBuilderExtraContainerEnv: items: - description: EnvVar represents an environment variable present in - a Container. + description: EnvVar represents an environment variable present in a Container. properties: name: description: Name of the environment variable. Must be a C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' + description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' type: string valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. + description: Source for the environment variable's value. Cannot be used if value is not empty. properties: configMapKeyRef: description: Selects a key of a ConfigMap. @@ -270,51 +245,39 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' type: string optional: - description: Specify whether the ConfigMap or its key - must be defined + description: Specify whether the ConfigMap or its key must be defined type: boolean required: - key type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' + description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' properties: apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". type: string fieldPath: - description: Path of the field to select in the specified - API version. + description: Path of the field to select in the specified API version. type: string required: - fieldPath type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' properties: containerName: - description: 'Container name: required for volumes, - optional for env vars' + description: 'Container name: required for volumes, optional for env vars' type: string divisor: anyOf: - type: integer - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" + description: Specifies the output format of the exposed resources, defaults to "1" pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true resource: @@ -328,16 +291,13 @@ spec: description: Selects a key of a secret in the pod's namespace properties: key: - description: The key of the secret to select from. Must - be a valid secret key. + description: The key of the secret to select from. Must be a valid secret key. type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' type: string optional: - description: Specify whether the Secret or its key must - be defined + description: Specify whether the Secret or its key must be defined type: boolean required: - key @@ -365,61 +325,29 @@ spec: description: Affinity is a group of affinity scheduling rules. properties: nodeAffinity: - description: Describes node affinity scheduling rules for - the pod. + description: Describes node affinity scheduling rules for the pod. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the affinity expressions specified - by this field, but it may choose a node that violates - one or more of the expressions. The node that is most - preferred is the one with the greatest sum of weights, - i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node matches the corresponding matchExpressions; - the node(s) with the highest sum are the most preferred. + description: The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred. items: - description: An empty preferred scheduling term matches - all objects with implicit weight 0 (i.e. it's a no-op). - A null preferred scheduling term matches no objects - (i.e. is also a no-op). + description: An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: - description: A node selector term, associated with - the corresponding weight. + description: A node selector term, associated with the corresponding weight. properties: matchExpressions: - description: A list of node selector requirements - by node's labels. + description: A list of node selector requirements by node's labels. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: The label key that the selector - applies to. + description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. items: type: string type: array @@ -429,35 +357,18 @@ spec: type: object type: array matchFields: - description: A list of node selector requirements - by node's fields. + description: A list of node selector requirements by node's fields. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: The label key that the selector - applies to. + description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. items: type: string type: array @@ -469,8 +380,7 @@ spec: type: object x-kubernetes-map-type: atomic weight: - description: Weight associated with matching the - corresponding nodeSelectorTerm, in the range 1-100. + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. format: int32 type: integer required: @@ -479,53 +389,26 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the affinity requirements - specified by this field cease to be met at some point - during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from - its node. + description: If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: - description: Required. A list of node selector terms. - The terms are ORed. + description: Required. A list of node selector terms. The terms are ORed. items: - description: A null or empty node selector term - matches no objects. The requirements of them are - ANDed. The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. + description: A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: - description: A list of node selector requirements - by node's labels. + description: A list of node selector requirements by node's labels. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: The label key that the selector - applies to. + description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. items: type: string type: array @@ -535,35 +418,18 @@ spec: type: object type: array matchFields: - description: A list of node selector requirements - by node's fields. + description: A list of node selector requirements by node's fields. items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: The label key that the selector - applies to. + description: The label key that the selector applies to. type: string operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. + description: An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. items: type: string type: array @@ -581,65 +447,32 @@ spec: x-kubernetes-map-type: atomic type: object podAffinity: - description: Describes pod affinity scheduling rules (e.g. - co-locate this pod in the same node, zone, etc. as some - other pod(s)). + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the affinity expressions specified - by this field, but it may choose a node that violates - one or more of the expressions. The node that is most - preferred is the one with the greatest sum of weights, - i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum are - the most preferred. + description: The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) properties: podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. + description: Required. A pod affinity term, associated with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: A label query over a set of resources, in this case pods. properties: matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key - that the selector applies to. + description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array @@ -651,53 +484,26 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key - that the selector applies to. + description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array @@ -709,42 +515,23 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. - Empty topologyKey is not allowed. + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching the - corresponding podAffinityTerm, in the range 1-100. + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. format: int32 type: integer required: @@ -753,56 +540,26 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the affinity requirements - specified by this field cease to be met at some point - during pod execution (e.g. due to a pod label update), - the system may or may not try to eventually evict the - pod from its node. When there are multiple elements, - the lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not - co-located (anti-affinity) with, where co-located - is defined as running on a node whose value of the - label with key matches that of any node - on which a pod of the set of pods is running + description: Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: A label query over a set of resources, in this case pods. properties: matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key that - the selector applies to. + description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array @@ -814,52 +571,26 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key that - the selector applies to. + description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array @@ -871,33 +602,17 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey @@ -905,65 +620,32 @@ spec: type: array type: object podAntiAffinity: - description: Describes pod anti-affinity scheduling rules - (e.g. avoid putting this pod in the same node, zone, etc. - as some other pod(s)). + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node that - violates one or more of the expressions. The node that - is most preferred is the one with the greatest sum of - weights, i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - anti-affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum are - the most preferred. + description: The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) properties: podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. + description: Required. A pod affinity term, associated with the corresponding weight. properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: A label query over a set of resources, in this case pods. properties: matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key - that the selector applies to. + description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array @@ -975,53 +657,26 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key - that the selector applies to. + description: key is the label key that the selector applies to. type: string operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array @@ -1033,42 +688,23 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace". + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. - Empty topologyKey is not allowed. + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching the - corresponding podAffinityTerm, in the range 1-100. + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. format: int32 type: integer required: @@ -1077,56 +713,26 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, the pod - will not be scheduled onto the node. If the anti-affinity - requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod - label update), the system may or may not try to eventually - evict the pod from its node. When there are multiple - elements, the lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not - co-located (anti-affinity) with, where co-located - is defined as running on a node whose value of the - label with key matches that of any node - on which a pod of the set of pods is running + description: Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running properties: labelSelector: - description: A label query over a set of resources, - in this case pods. + description: A label query over a set of resources, in this case pods. properties: matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key that - the selector applies to. + description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array @@ -1138,52 +744,26 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key that - the selector applies to. + description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array @@ -1195,33 +775,17 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. type: string required: - topologyKey @@ -1233,84 +797,54 @@ spec: additionalProperties: type: string type: object + priorityClassName: + type: string schedulerName: type: string serviceAccountName: type: string tolerations: items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . + description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . properties: effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, allowed - values are NoSchedule, PreferNoSchedule and NoExecute. + description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. type: string key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match - all values and all keys. + description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. type: string operator: - description: Operator represents a key's relationship to - the value. Valid operators are Exists and Equal. Defaults - to Equal. Exists is equivalent to wildcard for value, - so that a pod can tolerate all taints of a particular - category. + description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. type: string tolerationSeconds: - description: TolerationSeconds represents the period of - time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the taint - forever (do not evict). Zero and negative values will - be treated as 0 (evict immediately) by the system. + description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. format: int64 type: integer value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. + description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. type: string type: object type: array topologySpreadConstraints: items: - description: TopologySpreadConstraint specifies how to spread - matching pods among the given topology. + description: TopologySpreadConstraint specifies how to spread matching pods among the given topology. properties: labelSelector: - description: LabelSelector is used to find matching pods. - Pods that match this label selector are counted to determine - the number of pods in their corresponding topology domain. + description: LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain. properties: matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key that the selector - applies to. + description: key is the label key that the selector applies to. type: string operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists - or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array @@ -1322,127 +856,35 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic matchLabelKeys: - description: MatchLabelKeys is a set of pod label keys to - select the pods over which spreading will be calculated. - The keys are used to lookup values from the incoming pod - labels, those key-value labels are ANDed with labelSelector - to select the group of existing pods over which spreading - will be calculated for the incoming pod. Keys that don't - exist in the incoming pod labels will be ignored. A null - or empty list means only match against labelSelector. + description: MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector. items: type: string type: array x-kubernetes-list-type: atomic maxSkew: - description: 'MaxSkew describes the degree to which pods - may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, - it is the maximum permitted difference between the number - of matching pods in the target topology and the global - minimum. The global minimum is the minimum number of matching - pods in an eligible domain or zero if the number of eligible - domains is less than MinDomains. For example, in a 3-zone - cluster, MaxSkew is set to 1, and pods with the same labelSelector - spread as 2/2/1: In this case, the global minimum is 1. - | zone1 | zone2 | zone3 | | P P | P P | P | - - if MaxSkew is 1, incoming pod can only be scheduled to - zone3 to become 2/2/2; scheduling it onto zone1(zone2) - would make the ActualSkew(3-1) on zone1(zone2) violate - MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled - onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, - it is used to give higher precedence to topologies that - satisfy it. It''s a required field. Default value is 1 - and 0 is not allowed.' + description: 'MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It''s a required field. Default value is 1 and 0 is not allowed.' format: int32 type: integer minDomains: - description: "MinDomains indicates a minimum number of eligible - domains. When the number of eligible domains with matching - topology keys is less than minDomains, Pod Topology Spread - treats \"global minimum\" as 0, and then the calculation - of Skew is performed. And when the number of eligible - domains with matching topology keys equals or greater - than minDomains, this value has no effect on scheduling. - As a result, when the number of eligible domains is less - than minDomains, scheduler won't schedule more than maxSkew - Pods to those domains. If value is nil, the constraint - behaves as if MinDomains is equal to 1. Valid values are - integers greater than 0. When value is not nil, WhenUnsatisfiable - must be DoNotSchedule. \n For example, in a 3-zone cluster, - MaxSkew is set to 2, MinDomains is set to 5 and pods with - the same labelSelector spread as 2/2/2: | zone1 | zone2 - | zone3 | | P P | P P | P P | The number of domains - is less than 5(MinDomains), so \"global minimum\" is treated - as 0. In this situation, new pod with the same labelSelector - cannot be scheduled, because computed skew will be 3(3 - - 0) if new Pod is scheduled to any of the three zones, - it will violate MaxSkew. \n This is a beta field and requires - the MinDomainsInPodTopologySpread feature gate to be enabled - (enabled by default)." + description: "MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats \"global minimum\" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won't schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule. \n For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | | P P | P P | P P | The number of domains is less than 5(MinDomains), so \"global minimum\" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew. \n This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default)." format: int32 type: integer nodeAffinityPolicy: - description: "NodeAffinityPolicy indicates how we will treat - Pod's nodeAffinity/nodeSelector when calculating pod topology - spread skew. Options are: - Honor: only nodes matching - nodeAffinity/nodeSelector are included in the calculations. - - Ignore: nodeAffinity/nodeSelector are ignored. All nodes - are included in the calculations. \n If this value is - nil, the behavior is equivalent to the Honor policy. This - is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread - feature flag." + description: "NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. \n If this value is nil, the behavior is equivalent to the Honor policy. This is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread feature flag." type: string nodeTaintsPolicy: - description: "NodeTaintsPolicy indicates how we will treat - node taints when calculating pod topology spread skew. - Options are: - Honor: nodes without taints, along with - tainted nodes for which the incoming pod has a toleration, - are included. - Ignore: node taints are ignored. All nodes - are included. \n If this value is nil, the behavior is - equivalent to the Ignore policy. This is a alpha-level - feature enabled by the NodeInclusionPolicyInPodTopologySpread - feature flag." + description: "NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included. \n If this value is nil, the behavior is equivalent to the Ignore policy. This is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread feature flag." type: string topologyKey: - description: TopologyKey is the key of node labels. Nodes - that have a label with this key and identical values are - considered to be in the same topology. We consider each - as a "bucket", and try to put balanced number - of pods into each bucket. We define a domain as a particular - instance of a topology. Also, we define an eligible domain - as a domain whose nodes meet the requirements of nodeAffinityPolicy - and nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname", - each Node is a domain of that topology. And, if TopologyKey - is "topology.kubernetes.io/zone", each zone is a domain - of that topology. It's a required field. + description: TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each as a "bucket", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. It's a required field. type: string whenUnsatisfiable: - description: 'WhenUnsatisfiable indicates how to deal with - a pod if it doesn''t satisfy the spread constraint. - - DoNotSchedule (default) tells the scheduler not to schedule - it. - ScheduleAnyway tells the scheduler to schedule the - pod in any location, but giving higher precedence to topologies - that would help reduce the skew. A constraint is considered - "Unsatisfiable" for an incoming pod if and only if every - possible node assignment for that pod would violate "MaxSkew" - on some topology. For example, in a 3-zone cluster, MaxSkew - is set to 1, and pods with the same labelSelector spread - as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | - If WhenUnsatisfiable is set to DoNotSchedule, incoming - pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) - as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). - In other words, the cluster can still be imbalanced, but - scheduler won''t make it *more* imbalanced. It''s a required - field.' + description: 'WhenUnsatisfiable indicates how to deal with a pod if it doesn''t satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location, but giving higher precedence to topologies that would help reduce the skew. A constraint is considered "Unsatisfiable" for an incoming pod if and only if every possible node assignment for that pod would violate "MaxSkew" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won''t make it *more* imbalanced. It''s a required field.' type: string required: - maxSkew @@ -1462,6 +904,8 @@ spec: - tag type: object type: array + ociRegistryInsecure: + type: boolean runners: items: properties: @@ -1484,48 +928,25 @@ spec: description: BentoRequestStatus defines the observed state of BentoRequest properties: conditions: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. + description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ @@ -1538,11 +959,7 @@ spec: - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1555,40 +972,32 @@ spec: type: object type: array imageBuilderPodStatus: - description: PodStatus represents information about the status of - a pod. Status may trail the actual state of a system, especially - if the node that hosts the pod cannot contact the control plane. + description: PodStatus represents information about the status of a pod. Status may trail the actual state of a system, especially if the node that hosts the pod cannot contact the control plane. properties: conditions: description: 'Current service state of pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions' items: - description: PodCondition contains details for the current condition - of this pod. + description: PodCondition contains details for the current condition of this pod. properties: lastProbeTime: description: Last time we probed the condition. format: date-time type: string lastTransitionTime: - description: Last time the condition transitioned from one - status to another. + description: Last time the condition transitioned from one status to another. format: date-time type: string message: - description: Human-readable message indicating details about - last transition. + description: Human-readable message indicating details about last transition. type: string reason: - description: Unique, one-word, CamelCase reason for the - condition's last transition. + description: Unique, one-word, CamelCase reason for the condition's last transition. type: string status: - description: 'Status is the status of the condition. Can - be True, False, Unknown. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions' + description: 'Status is the status of the condition. Can be True, False, Unknown. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions' type: string type: - description: 'Type is the type of the condition. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions' + description: 'Type is the type of the condition. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions' type: string required: - status @@ -1596,32 +1005,27 @@ spec: type: object type: array containerStatuses: - description: 'The list has one entry per container in the manifest. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status' + description: 'The list has one entry per container in the manifest. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status' items: - description: ContainerStatus contains details for the current - status of this container. + description: ContainerStatus contains details for the current status of this container. properties: containerID: description: Container's ID in the format '://'. type: string image: - description: 'The image the container is running. More info: - https://kubernetes.io/docs/concepts/containers/images.' + description: 'The image the container is running. More info: https://kubernetes.io/docs/concepts/containers/images.' type: string imageID: description: ImageID of the container's image. type: string lastState: - description: Details about the container's last termination - condition. + description: Details about the container's last termination condition. properties: running: description: Details about a running container properties: startedAt: - description: Time at which the container was last - (re-)started + description: Time at which the container was last (re-)started format: date-time type: string type: object @@ -1632,8 +1036,7 @@ spec: description: Container's ID in the format '://' type: string exitCode: - description: Exit status from the last termination - of the container + description: Exit status from the last termination of the container format: int32 type: integer finishedAt: @@ -1641,21 +1044,17 @@ spec: format: date-time type: string message: - description: Message regarding the last termination - of the container + description: Message regarding the last termination of the container type: string reason: - description: (brief) reason from the last termination - of the container + description: (brief) reason from the last termination of the container type: string signal: - description: Signal from the last termination of - the container + description: Signal from the last termination of the container format: int32 type: integer startedAt: - description: Time at which previous execution of - the container started + description: Time at which previous execution of the container started format: date-time type: string required: @@ -1665,35 +1064,25 @@ spec: description: Details about a waiting container properties: message: - description: Message regarding why the container - is not yet running. + description: Message regarding why the container is not yet running. type: string reason: - description: (brief) reason the container is not - yet running. + description: (brief) reason the container is not yet running. type: string type: object type: object name: - description: This must be a DNS_LABEL. Each container in - a pod must have a unique name. Cannot be updated. + description: This must be a DNS_LABEL. Each container in a pod must have a unique name. Cannot be updated. type: string ready: - description: Specifies whether the container has passed - its readiness probe. + description: Specifies whether the container has passed its readiness probe. type: boolean restartCount: - description: The number of times the container has been - restarted. + description: The number of times the container has been restarted. format: int32 type: integer started: - description: Specifies whether the container has passed - its startup probe. Initialized as false, becomes true - after startupProbe is considered successful. Resets to - false when the container is restarted, or if kubelet loses - state temporarily. Is always true when no startupProbe - is defined. + description: Specifies whether the container has passed its startup probe. Initialized as false, becomes true after startupProbe is considered successful. Resets to false when the container is restarted, or if kubelet loses state temporarily. Is always true when no startupProbe is defined. type: boolean state: description: Details about the container's current condition. @@ -1702,8 +1091,7 @@ spec: description: Details about a running container properties: startedAt: - description: Time at which the container was last - (re-)started + description: Time at which the container was last (re-)started format: date-time type: string type: object @@ -1714,8 +1102,7 @@ spec: description: Container's ID in the format '://' type: string exitCode: - description: Exit status from the last termination - of the container + description: Exit status from the last termination of the container format: int32 type: integer finishedAt: @@ -1723,21 +1110,17 @@ spec: format: date-time type: string message: - description: Message regarding the last termination - of the container + description: Message regarding the last termination of the container type: string reason: - description: (brief) reason from the last termination - of the container + description: (brief) reason from the last termination of the container type: string signal: - description: Signal from the last termination of - the container + description: Signal from the last termination of the container format: int32 type: integer startedAt: - description: Time at which previous execution of - the container started + description: Time at which previous execution of the container started format: date-time type: string required: @@ -1747,12 +1130,10 @@ spec: description: Details about a waiting container properties: message: - description: Message regarding why the container - is not yet running. + description: Message regarding why the container is not yet running. type: string reason: - description: (brief) reason the container is not - yet running. + description: (brief) reason the container is not yet running. type: string type: object type: object @@ -1765,32 +1146,27 @@ spec: type: object type: array ephemeralContainerStatuses: - description: Status for any ephemeral containers that have run - in this pod. + description: Status for any ephemeral containers that have run in this pod. items: - description: ContainerStatus contains details for the current - status of this container. + description: ContainerStatus contains details for the current status of this container. properties: containerID: description: Container's ID in the format '://'. type: string image: - description: 'The image the container is running. More info: - https://kubernetes.io/docs/concepts/containers/images.' + description: 'The image the container is running. More info: https://kubernetes.io/docs/concepts/containers/images.' type: string imageID: description: ImageID of the container's image. type: string lastState: - description: Details about the container's last termination - condition. + description: Details about the container's last termination condition. properties: running: description: Details about a running container properties: startedAt: - description: Time at which the container was last - (re-)started + description: Time at which the container was last (re-)started format: date-time type: string type: object @@ -1801,8 +1177,7 @@ spec: description: Container's ID in the format '://' type: string exitCode: - description: Exit status from the last termination - of the container + description: Exit status from the last termination of the container format: int32 type: integer finishedAt: @@ -1810,21 +1185,17 @@ spec: format: date-time type: string message: - description: Message regarding the last termination - of the container + description: Message regarding the last termination of the container type: string reason: - description: (brief) reason from the last termination - of the container + description: (brief) reason from the last termination of the container type: string signal: - description: Signal from the last termination of - the container + description: Signal from the last termination of the container format: int32 type: integer startedAt: - description: Time at which previous execution of - the container started + description: Time at which previous execution of the container started format: date-time type: string required: @@ -1834,35 +1205,25 @@ spec: description: Details about a waiting container properties: message: - description: Message regarding why the container - is not yet running. + description: Message regarding why the container is not yet running. type: string reason: - description: (brief) reason the container is not - yet running. + description: (brief) reason the container is not yet running. type: string type: object type: object name: - description: This must be a DNS_LABEL. Each container in - a pod must have a unique name. Cannot be updated. + description: This must be a DNS_LABEL. Each container in a pod must have a unique name. Cannot be updated. type: string ready: - description: Specifies whether the container has passed - its readiness probe. + description: Specifies whether the container has passed its readiness probe. type: boolean restartCount: - description: The number of times the container has been - restarted. + description: The number of times the container has been restarted. format: int32 type: integer started: - description: Specifies whether the container has passed - its startup probe. Initialized as false, becomes true - after startupProbe is considered successful. Resets to - false when the container is restarted, or if kubelet loses - state temporarily. Is always true when no startupProbe - is defined. + description: Specifies whether the container has passed its startup probe. Initialized as false, becomes true after startupProbe is considered successful. Resets to false when the container is restarted, or if kubelet loses state temporarily. Is always true when no startupProbe is defined. type: boolean state: description: Details about the container's current condition. @@ -1871,8 +1232,7 @@ spec: description: Details about a running container properties: startedAt: - description: Time at which the container was last - (re-)started + description: Time at which the container was last (re-)started format: date-time type: string type: object @@ -1883,8 +1243,7 @@ spec: description: Container's ID in the format '://' type: string exitCode: - description: Exit status from the last termination - of the container + description: Exit status from the last termination of the container format: int32 type: integer finishedAt: @@ -1892,21 +1251,17 @@ spec: format: date-time type: string message: - description: Message regarding the last termination - of the container + description: Message regarding the last termination of the container type: string reason: - description: (brief) reason from the last termination - of the container + description: (brief) reason from the last termination of the container type: string signal: - description: Signal from the last termination of - the container + description: Signal from the last termination of the container format: int32 type: integer startedAt: - description: Time at which previous execution of - the container started + description: Time at which previous execution of the container started format: date-time type: string required: @@ -1916,12 +1271,10 @@ spec: description: Details about a waiting container properties: message: - description: Message regarding why the container - is not yet running. + description: Message regarding why the container is not yet running. type: string reason: - description: (brief) reason the container is not - yet running. + description: (brief) reason the container is not yet running. type: string type: object type: object @@ -1934,38 +1287,30 @@ spec: type: object type: array hostIP: - description: IP address of the host to which the pod is assigned. - Empty if not yet scheduled. + description: IP address of the host to which the pod is assigned. Empty if not yet scheduled. type: string initContainerStatuses: - description: 'The list has one entry per init container in the - manifest. The most recent successful init container will have - ready = true, the most recently started container will have - startTime set. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status' + description: 'The list has one entry per init container in the manifest. The most recent successful init container will have ready = true, the most recently started container will have startTime set. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status' items: - description: ContainerStatus contains details for the current - status of this container. + description: ContainerStatus contains details for the current status of this container. properties: containerID: description: Container's ID in the format '://'. type: string image: - description: 'The image the container is running. More info: - https://kubernetes.io/docs/concepts/containers/images.' + description: 'The image the container is running. More info: https://kubernetes.io/docs/concepts/containers/images.' type: string imageID: description: ImageID of the container's image. type: string lastState: - description: Details about the container's last termination - condition. + description: Details about the container's last termination condition. properties: running: description: Details about a running container properties: startedAt: - description: Time at which the container was last - (re-)started + description: Time at which the container was last (re-)started format: date-time type: string type: object @@ -1976,8 +1321,7 @@ spec: description: Container's ID in the format '://' type: string exitCode: - description: Exit status from the last termination - of the container + description: Exit status from the last termination of the container format: int32 type: integer finishedAt: @@ -1985,21 +1329,17 @@ spec: format: date-time type: string message: - description: Message regarding the last termination - of the container + description: Message regarding the last termination of the container type: string reason: - description: (brief) reason from the last termination - of the container + description: (brief) reason from the last termination of the container type: string signal: - description: Signal from the last termination of - the container + description: Signal from the last termination of the container format: int32 type: integer startedAt: - description: Time at which previous execution of - the container started + description: Time at which previous execution of the container started format: date-time type: string required: @@ -2009,35 +1349,25 @@ spec: description: Details about a waiting container properties: message: - description: Message regarding why the container - is not yet running. + description: Message regarding why the container is not yet running. type: string reason: - description: (brief) reason the container is not - yet running. + description: (brief) reason the container is not yet running. type: string type: object type: object name: - description: This must be a DNS_LABEL. Each container in - a pod must have a unique name. Cannot be updated. + description: This must be a DNS_LABEL. Each container in a pod must have a unique name. Cannot be updated. type: string ready: - description: Specifies whether the container has passed - its readiness probe. + description: Specifies whether the container has passed its readiness probe. type: boolean restartCount: - description: The number of times the container has been - restarted. + description: The number of times the container has been restarted. format: int32 type: integer started: - description: Specifies whether the container has passed - its startup probe. Initialized as false, becomes true - after startupProbe is considered successful. Resets to - false when the container is restarted, or if kubelet loses - state temporarily. Is always true when no startupProbe - is defined. + description: Specifies whether the container has passed its startup probe. Initialized as false, becomes true after startupProbe is considered successful. Resets to false when the container is restarted, or if kubelet loses state temporarily. Is always true when no startupProbe is defined. type: boolean state: description: Details about the container's current condition. @@ -2046,8 +1376,7 @@ spec: description: Details about a running container properties: startedAt: - description: Time at which the container was last - (re-)started + description: Time at which the container was last (re-)started format: date-time type: string type: object @@ -2058,8 +1387,7 @@ spec: description: Container's ID in the format '://' type: string exitCode: - description: Exit status from the last termination - of the container + description: Exit status from the last termination of the container format: int32 type: integer finishedAt: @@ -2067,21 +1395,17 @@ spec: format: date-time type: string message: - description: Message regarding the last termination - of the container + description: Message regarding the last termination of the container type: string reason: - description: (brief) reason from the last termination - of the container + description: (brief) reason from the last termination of the container type: string signal: - description: Signal from the last termination of - the container + description: Signal from the last termination of the container format: int32 type: integer startedAt: - description: Time at which previous execution of - the container started + description: Time at which previous execution of the container started format: date-time type: string required: @@ -2091,12 +1415,10 @@ spec: description: Details about a waiting container properties: message: - description: Message regarding why the container - is not yet running. + description: Message regarding why the container is not yet running. type: string reason: - description: (brief) reason the container is not - yet running. + description: (brief) reason the container is not yet running. type: string type: object type: object @@ -2109,74 +1431,35 @@ spec: type: object type: array message: - description: A human readable message indicating details about - why the pod is in this condition. + description: A human readable message indicating details about why the pod is in this condition. type: string nominatedNodeName: - description: nominatedNodeName is set only when this pod preempts - other pods on the node, but it cannot be scheduled right away - as preemption victims receive their graceful termination periods. - This field does not guarantee that the pod will be scheduled - on this node. Scheduler may decide to place the pod elsewhere - if other nodes become available sooner. Scheduler may also decide - to give the resources on this node to a higher priority pod - that is created after preemption. As a result, this field may - be different than PodSpec.nodeName when the pod is scheduled. + description: nominatedNodeName is set only when this pod preempts other pods on the node, but it cannot be scheduled right away as preemption victims receive their graceful termination periods. This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to give the resources on this node to a higher priority pod that is created after preemption. As a result, this field may be different than PodSpec.nodeName when the pod is scheduled. type: string phase: - description: "The phase of a Pod is a simple, high-level summary - of where the Pod is in its lifecycle. The conditions array, - the reason and message fields, and the individual container - status arrays contain more detail about the pod's status. There - are five possible phase values: \n Pending: The pod has been - accepted by the Kubernetes system, but one or more of the container - images has not been created. This includes time before being - scheduled as well as time spent downloading images over the - network, which could take a while. Running: The pod has been - bound to a node, and all of the containers have been created. - At least one container is still running, or is in the process - of starting or restarting. Succeeded: All containers in the - pod have terminated in success, and will not be restarted. Failed: - All containers in the pod have terminated, and at least one - container has terminated in failure. The container either exited - with non-zero status or was terminated by the system. Unknown: - For some reason the state of the pod could not be obtained, - typically due to an error in communicating with the host of - the pod. \n More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase" + description: "The phase of a Pod is a simple, high-level summary of where the Pod is in its lifecycle. The conditions array, the reason and message fields, and the individual container status arrays contain more detail about the pod's status. There are five possible phase values: \n Pending: The pod has been accepted by the Kubernetes system, but one or more of the container images has not been created. This includes time before being scheduled as well as time spent downloading images over the network, which could take a while. Running: The pod has been bound to a node, and all of the containers have been created. At least one container is still running, or is in the process of starting or restarting. Succeeded: All containers in the pod have terminated in success, and will not be restarted. Failed: All containers in the pod have terminated, and at least one container has terminated in failure. The container either exited with non-zero status or was terminated by the system. Unknown: For some reason the state of the pod could not be obtained, typically due to an error in communicating with the host of the pod. \n More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase" type: string podIP: - description: IP address allocated to the pod. Routable at least - within the cluster. Empty if not yet allocated. + description: IP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated. type: string podIPs: - description: podIPs holds the IP addresses allocated to the pod. - If this field is specified, the 0th entry must match the podIP - field. Pods may be allocated at most 1 value for each of IPv4 - and IPv6. This list is empty if no IPs have been allocated yet. + description: podIPs holds the IP addresses allocated to the pod. If this field is specified, the 0th entry must match the podIP field. Pods may be allocated at most 1 value for each of IPv4 and IPv6. This list is empty if no IPs have been allocated yet. items: - description: "IP address information for entries in the (plural) - PodIPs field. Each entry includes: \n IP: An IP address allocated - to the pod. Routable at least within the cluster." + description: "IP address information for entries in the (plural) PodIPs field. Each entry includes: \n IP: An IP address allocated to the pod. Routable at least within the cluster." properties: ip: - description: ip is an IP address (IPv4 or IPv6) assigned - to the pod + description: ip is an IP address (IPv4 or IPv6) assigned to the pod type: string type: object type: array qosClass: - description: 'The Quality of Service (QOS) classification assigned - to the pod based on resource requirements See PodQOSClass type - for available QOS classes More info: https://git.k8s.io/community/contributors/design-proposals/node/resource-qos.md' + description: 'The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://git.k8s.io/community/contributors/design-proposals/node/resource-qos.md' type: string reason: - description: A brief CamelCase message indicating details about - why the pod is in this state. e.g. 'Evicted' + description: A brief CamelCase message indicating details about why the pod is in this state. e.g. 'Evicted' type: string startTime: - description: RFC 3339 date and time at which the object was acknowledged - by the Kubelet. This is before the Kubelet pulled the container - image(s) for the pod. + description: RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod. format: date-time type: string type: object diff --git a/helm/yatai-image-builder-crds/values.yaml b/helm/yatai-image-builder-crds/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/helm/yatai-image-builder/templates/_helpers.tpl b/helm/yatai-image-builder/templates/_helpers.tpl index ed46449..a4d3306 100644 --- a/helm/yatai-image-builder/templates/_helpers.tpl +++ b/helm/yatai-image-builder/templates/_helpers.tpl @@ -36,7 +36,11 @@ yatai-common-env {{- end }} {{- define "yatai-image-builder.yatai-rolename-in-yatai-system-namespace" -}} -yatai-common-env +yatai-role-for-yatai-image-builder +{{- end }} + +{{- define "yatai-image-builder.yatai-with-yatai-image-builder-rolename" -}} +yatai-with-yatai-image-builder {{- end }} {{/* diff --git a/helm/yatai-image-builder/templates/clusterrole.yaml b/helm/yatai-image-builder/templates/clusterrole.yaml index 863309a..d2e9cff 100644 --- a/helm/yatai-image-builder/templates/clusterrole.yaml +++ b/helm/yatai-image-builder/templates/clusterrole.yaml @@ -39,6 +39,18 @@ rules: - bentoes/status verbs: - update +- apiGroups: + - "batch" + resources: + - jobs + verbs: + - create + - delete + - update + - patch + - get + - list + - watch - apiGroups: - "" resources: diff --git a/helm/yatai-image-builder/templates/clusterrolebinding.yaml b/helm/yatai-image-builder/templates/clusterrolebinding.yaml index 49a34e3..94d1079 100644 --- a/helm/yatai-image-builder/templates/clusterrolebinding.yaml +++ b/helm/yatai-image-builder/templates/clusterrolebinding.yaml @@ -10,4 +10,3 @@ subjects: - kind: ServiceAccount name: {{ include "yatai-image-builder.serviceAccountName" . }} namespace: {{ .Release.Namespace }} - diff --git a/helm/yatai-image-builder/templates/role-in-yatai-system-namespace.yaml b/helm/yatai-image-builder/templates/role-in-yatai-system-namespace.yaml index 9f82fd7..5c528c5 100644 --- a/helm/yatai-image-builder/templates/role-in-yatai-system-namespace.yaml +++ b/helm/yatai-image-builder/templates/role-in-yatai-system-namespace.yaml @@ -8,6 +8,9 @@ rules: - "" resources: - secrets + resourceNames: + - {{ include "yatai-image-builder.yatai-common-envname" . }} + - {{ include "yatai-image-builder.shared-envname" . }} verbs: - get - list diff --git a/helm/yatai-image-builder/templates/role-yatai-in-yatai-system-namespace.yaml b/helm/yatai-image-builder/templates/role-yatai-in-yatai-system-namespace.yaml index a1026f5..da9ce8b 100644 --- a/helm/yatai-image-builder/templates/role-yatai-in-yatai-system-namespace.yaml +++ b/helm/yatai-image-builder/templates/role-yatai-in-yatai-system-namespace.yaml @@ -8,6 +8,9 @@ rules: - "" resources: - secrets + resourceNames: + - {{ include "yatai-image-builder.yatai-common-envname" . }} + - {{ include "yatai-image-builder.shared-envname" . }} verbs: - get - list diff --git a/helm/yatai-image-builder/templates/role-yatai-with-yatai-image-builder.yaml b/helm/yatai-image-builder/templates/role-yatai-with-yatai-image-builder.yaml index ae47bb7..226cf21 100644 --- a/helm/yatai-image-builder/templates/role-yatai-with-yatai-image-builder.yaml +++ b/helm/yatai-image-builder/templates/role-yatai-with-yatai-image-builder.yaml @@ -1,7 +1,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: yatai-with-yatai-image-builder + name: {{ include "yatai-image-builder.yatai-with-yatai-image-builder-rolename" . }} namespace: {{ .Release.Namespace }} rules: - apiGroups: @@ -24,6 +24,8 @@ rules: - "" resources: - configmaps + resourceNames: + - yatai-image-builder-config verbs: - get - list @@ -36,6 +38,14 @@ rules: - get - list - watch +- apiGroups: + - events.k8s.io + resources: + - events + verbs: + - get + - list + - watch - apiGroups: - "" resources: @@ -55,6 +65,28 @@ rules: - get - list - watch +- apiGroups: + - "" + resources: + - pods/attach + verbs: + - create + - update + - patch + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/ephemeralcontainers + verbs: + - create + - update + - patch + - get + - list + - watch - apiGroups: - "" resources: @@ -67,6 +99,9 @@ rules: - "" resources: - secrets + resourceNames: + - {{ include "yatai-image-builder.envname" . }} + - yatai-image-builder-build-args verbs: - get - list diff --git a/helm/yatai-image-builder/templates/role.yaml b/helm/yatai-image-builder/templates/role.yaml index 05df604..0531571 100644 --- a/helm/yatai-image-builder/templates/role.yaml +++ b/helm/yatai-image-builder/templates/role.yaml @@ -8,6 +8,8 @@ rules: - "" resources: - secrets + resourceNames: + - yatai-image-builder-build-args verbs: - get - list @@ -16,6 +18,8 @@ rules: - "" resources: - configmaps + resourceNames: + - yatai-image-builder-config verbs: - get - list diff --git a/helm/yatai-image-builder/templates/rolebinding-yatai-with-yatai-image-builder.yaml b/helm/yatai-image-builder/templates/rolebinding-yatai-with-yatai-image-builder.yaml index d70703d..2205d51 100644 --- a/helm/yatai-image-builder/templates/rolebinding-yatai-with-yatai-image-builder.yaml +++ b/helm/yatai-image-builder/templates/rolebinding-yatai-with-yatai-image-builder.yaml @@ -1,12 +1,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: yatai-with-yatai-image-builder + name: {{ include "yatai-image-builder.yatai-with-yatai-image-builder-rolename" . }} namespace: {{ .Release.Namespace }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: yatai-with-yatai-image-builder + name: {{ include "yatai-image-builder.yatai-with-yatai-image-builder-rolename" . }} subjects: - kind: ServiceAccount name: {{ .Values.yataiSystem.serviceAccountName }} diff --git a/helm/yatai-image-builder/templates/secret-env.yaml b/helm/yatai-image-builder/templates/secret-env.yaml index 9f3aecd..d89e4cf 100644 --- a/helm/yatai-image-builder/templates/secret-env.yaml +++ b/helm/yatai-image-builder/templates/secret-env.yaml @@ -15,23 +15,25 @@ stringData: DOCKER_REGISTRY_SERVER: {{ .Values.dockerRegistry.server | quote }} DOCKER_REGISTRY_IN_CLUSTER_SERVER: {{ .Values.dockerRegistry.inClusterServer | quote }} DOCKER_REGISTRY_USERNAME: {{ .Values.dockerRegistry.username | quote }} - {{- if not .Values.dockerRegistry.passwordExistingSecretName }} + {{- if .Values.dockerRegistry.passwordExistingSecretName }} DOCKER_REGISTRY_PASSWORD: {{ .Values.dockerRegistry.password | quote }} + {{- else }} + DOCKER_REGISTRY_PASSWORD: null {{- end }} DOCKER_REGISTRY_SECURE: {{ .Values.dockerRegistry.secure | quote }} DOCKER_REGISTRY_BENTO_REPOSITORY_NAME: {{ .Values.dockerRegistry.bentoRepositoryName | quote }} - {{- if .Values.aws.accessKeyID }} - AWS_ACCESS_KEY_ID: {{ .Values.aws.accessKeyID | quote }} - {{- end }} - {{- if .Values.aws.secretAccessKey }} - AWS_SECRET_ACCESS_KEY: {{ .Values.aws.secretAccessKey | quote }} - {{- end }} + AWS_ACCESS_KEY_ID: {{ default "null" (.Values.aws.accessKeyID | quote) }} + AWS_SECRET_ACCESS_KEY: {{ default "null" (.Values.aws.secretAccessKey | quote) }} INTERNAL_IMAGES_BENTO_DOWNLOADER: {{ .Values.internalImages.bentoDownloader | quote }} INTERNAL_IMAGES_KANIKO: {{ .Values.internalImages.kaniko | quote }} + INTERNAL_IMAGES_BUILDKIT: {{ .Values.internalImages.buildkit | quote }} + INTERNAL_IMAGES_BUILDKIT_ROOTLESS: {{ .Values.internalImages.buildkitRootless | quote }} - {{- if .Values.dockerRegistry.useAWSECRWithIAMRole }} - AWS_ECR_WITH_IAM_ROLE: "true" - AWS_ECR_REGION: {{ .Values.dockerRegistry.awsECRRegion | quote }} - {{- end }} + AWS_ECR_WITH_IAM_ROLE: {{default "null" .Values.dockerRegistry.useAWSECRWithIAMRole }} + AWS_ECR_REGION: {{ default "null" (.Values.dockerRegistry.awsECRRegion | quote) }} + + BENTO_IMAGE_BUILD_ENGINE: {{ .Values.bentoImageBuildEngine | quote }} + + DISABLE_YATAI_COMPONENT_REGISTRATION: {{ .Values.disableYataiComponentRegistration | quote }} diff --git a/helm/yatai-image-builder/values.yaml b/helm/yatai-image-builder/values.yaml index c342a5c..f44a520 100644 --- a/helm/yatai-image-builder/values.yaml +++ b/helm/yatai-image-builder/values.yaml @@ -76,6 +76,8 @@ yatai: apiToken: '' clusterName: default +disableYataiComponentRegistration: false + dockerRegistry: server: '' inClusterServer: '' @@ -97,4 +99,8 @@ aws: internalImages: bentoDownloader: quay.io/bentoml/bento-downloader:0.0.1 - kaniko: quay.io/bentoml/kaniko:1.9.1 + kaniko: quay.io/bentoml/kaniko:debug + buildkit: quay.io/bentoml/buildkit:master + buildkitRootless: quay.io/bentoml/buildkit:master-rootless + +bentoImageBuildEngine: kaniko # options: kaniko, buildkit, buildkit-rootless diff --git a/scripts/quick-install-yatai-image-builder.sh b/scripts/quick-install-yatai-image-builder.sh index a289794..79c96f5 100755 --- a/scripts/quick-install-yatai-image-builder.sh +++ b/scripts/quick-install-yatai-image-builder.sh @@ -62,12 +62,21 @@ if ! command -v helm >/dev/null 2>&1; then exit 1 fi -echo "๐Ÿงช verifying that the yatai is running" -if ! kubectl -n yatai-system wait --for=condition=ready --timeout=10s pod -l app.kubernetes.io/name=yatai; then - echo "๐Ÿ˜ฑ yatai is not ready, please wait for it to be ready!" >&2 - exit 1 +YATAI_ENDPOINT=${YATAI_ENDPOINT:-http://yatai.yatai-system.svc.cluster.local} +if [ "${YATAI_ENDPOINT}" = "empty" ]; then + YATAI_ENDPOINT="" +fi + +YATAI_SERVICE_ACCOUNT=${YATAI_SERVICE_ACCOUNT:-yatai} + +if [ "${YATAI_ENDPOINT}" = "http://yatai.yatai-system.svc.cluster.local" ]; then + echo "๐Ÿงช verifying that the yatai is running" + if ! kubectl -n yatai-system wait --for=condition=ready --timeout=10s pod -l app.kubernetes.io/name=yatai; then + echo "๐Ÿ˜ฑ yatai is not ready, please wait for it to be ready!" >&2 + exit 1 + fi + echo "โœ… yatai is ready" fi -echo "โœ… yatai is ready" namespace=yatai-image-builder @@ -88,6 +97,8 @@ else echo "๐Ÿ˜€ cert-manager is already installed" fi +echo "๐Ÿ˜ด sleep 5s to make cert-manager pod created ๐Ÿคท" + echo "โณ waiting for cert-manager to be ready..." kubectl wait --for=condition=ready --timeout=600s pod -l app.kubernetes.io/instance=cert-manager -A echo "โœ… cert-manager is ready" @@ -187,51 +198,87 @@ DOCKER_REGISTRY_PASSWORD='' DOCKER_REGISTRY_SECURE=false DOCKER_REGISTRY_BENTO_REPOSITORY_NAME=yatai-bentos -helm_repo_name=bentoml -helm_repo_url=https://bentoml.github.io/helm-charts +USE_LOCAL_HELM_CHART=${USE_LOCAL_HELM_CHART:-false} -# check if DEVEL_HELM_REPO is true -if [ "${DEVEL_HELM_REPO}" = "true" ]; then - helm_repo_name=bentoml-devel - helm_repo_url=https://bentoml.github.io/helm-charts-devel -fi +if [ "${USE_LOCAL_HELM_CHART}" = "true" ]; then + YATAI_IMAGE_BUILDER_IMG_REGISTRY=${YATAI_IMAGE_BUILDER_IMG_REGISTRY:-quay.io/bentoml} + YATAI_IMAGE_BUILDER_IMG_REPO=${YATAI_IMAGE_BUILDER_IMG_REPO:-yatai-image-builder} + YATAI_IMAGE_BUILDER_IMG_TAG=${YATAI_IMAGE_BUILDER_IMG_TAG:-0.0.1} -UPGRADE_CRDS=${UPGRADE_CRDS:-false} + echo "๐Ÿค– installing yatai-image-builder-crds from local helm chart..." + helm upgrade --install yatai-image-builder-crds ./helm/yatai-image-builder-crds -n ${namespace} -if [ "${UPGRADE_CRDS}" = "true" ]; then - echo "๐Ÿค– installing yatai-image-builder CRDs..." - kubectl apply --server-side -f https://raw.githubusercontent.com/bentoml/yatai-image-builder/main/helm/yatai-image-builder/crds/bentorequest.yaml echo "โณ waiting for BentoRequest CRD to be established..." kubectl wait --for condition=established --timeout=120s crd/bentorequests.resources.yatai.ai echo "โœ… BentoRequest CRD are established" echo "โณ waiting for Bento CRD to be established..." - kubectl wait --for condition=established --timeout=120s crd/bentos.resources.yatai.ai + kubectl wait --for condition=established --timeout=120s crd/bentoes.resources.yatai.ai echo "โœ… Bento CRD are established" -fi -helm repo remove ${helm_repo_name} 2> /dev/null || true -helm repo add ${helm_repo_name} ${helm_repo_url} -helm repo update ${helm_repo_name} + echo "๐Ÿค– installing yatai-image-builder from local helm chart..." + helm upgrade --install yatai-image-builder ./helm/yatai-image-builder -n ${namespace} \ + --set registry=${YATAI_IMAGE_BUILDER_IMG_REGISTRY} \ + --set image.repository=${YATAI_IMAGE_BUILDER_IMG_REPO} \ + --set image.tag=${YATAI_IMAGE_BUILDER_IMG_TAG} \ + --set yatai.endpoint=${YATAI_ENDPOINT} \ + --set dockerRegistry.server=${DOCKER_REGISTRY_SERVER} \ + --set dockerRegistry.inClusterServer=${DOCKER_REGISTRY_IN_CLUSTER_SERVER} \ + --set dockerRegistry.username=${DOCKER_REGISTRY_USERNAME} \ + --set dockerRegistry.password=${DOCKER_REGISTRY_PASSWORD} \ + --set dockerRegistry.secure=${DOCKER_REGISTRY_SECURE} \ + --set dockerRegistry.bentoRepositoryName=${DOCKER_REGISTRY_BENTO_REPOSITORY_NAME} \ + --set aws.accessKeyID=${AWS_ACCESS_KEY_ID} \ + --set aws.secretAccessKeyExistingSecretName=${AWS_SECRET_ACCESS_KEY_EXISTING_SECRET_NAME} \ + --set aws.secretAccessKeyExistingSecretKey=${AWS_SECRET_ACCESS_KEY_EXISTING_SECRET_KEY} \ + --set bentoImageBuildEngine=buildkit-rootless +else + helm_repo_name=bentoml + helm_repo_url=https://bentoml.github.io/helm-charts -# if $VERSION is not set, use the latest version -if [ -z "$VERSION" ]; then - VERSION=$(helm search repo ${helm_repo_name} --devel="$DEVEL" -l | grep "${helm_repo_name}/yatai-image-builder " | awk '{print $2}' | head -n 1) -fi + # check if DEVEL_HELM_REPO is true + if [ "${DEVEL_HELM_REPO}" = "true" ]; then + helm_repo_name=bentoml-devel + helm_repo_url=https://bentoml.github.io/helm-charts-devel + fi -echo "๐Ÿค– installing yatai-image-builder ${VERSION} from helm repo ${helm_repo_name}..." -helm upgrade --install yatai-image-builder ${helm_repo_name}/yatai-image-builder -n ${namespace} \ - --set dockerRegistry.server=${DOCKER_REGISTRY_SERVER} \ - --set dockerRegistry.inClusterServer=${DOCKER_REGISTRY_IN_CLUSTER_SERVER} \ - --set dockerRegistry.username=${DOCKER_REGISTRY_USERNAME} \ - --set dockerRegistry.password=${DOCKER_REGISTRY_PASSWORD} \ - --set dockerRegistry.secure=${DOCKER_REGISTRY_SECURE} \ - --set dockerRegistry.bentoRepositoryName=${DOCKER_REGISTRY_BENTO_REPOSITORY_NAME} \ - --skip-crds=${UPGRADE_CRDS} \ - --version=${VERSION} \ - --devel=${DEVEL} + helm repo remove ${helm_repo_name} 2> /dev/null || true + helm repo add ${helm_repo_name} ${helm_repo_url} + helm repo update ${helm_repo_name} + + # if $VERSION is not set, use the latest version + if [ -z "$VERSION" ]; then + VERSION=$(helm search repo ${helm_repo_name} --devel="$DEVEL" -l | grep "${helm_repo_name}/yatai-image-builder " | awk '{print $2}' | head -n 1) + fi + + echo "๐Ÿค– installing yatai-image-builder-crds from helm repo ${helm_repo_url}..." + helm upgrade --install yatai-image-builder-crds yatai-image-builder-crds --repo ${helm_repo_url} -n ${namespace} --devel=${DEVEL} + + echo "โณ waiting for BentoRequest CRD to be established..." + kubectl wait --for condition=established --timeout=120s crd/bentorequests.resources.yatai.ai + echo "โœ… BentoRequest CRD are established" + echo "โณ waiting for Bento CRD to be established..." + kubectl wait --for condition=established --timeout=120s crd/bentoes.resources.yatai.ai + echo "โœ… Bento CRD are established" + + echo "๐Ÿค– installing yatai-image-builder ${VERSION} from helm repo ${helm_repo_url}..." + helm upgrade --install yatai-image-builder yatai-image-builder --repo ${helm_repo_url} -n ${namespace} \ + --set yatai.endpoint=${YATAI_ENDPOINT} \ + --set dockerRegistry.server=${DOCKER_REGISTRY_SERVER} \ + --set dockerRegistry.inClusterServer=${DOCKER_REGISTRY_IN_CLUSTER_SERVER} \ + --set dockerRegistry.username=${DOCKER_REGISTRY_USERNAME} \ + --set dockerRegistry.password=${DOCKER_REGISTRY_PASSWORD} \ + --set dockerRegistry.secure=${DOCKER_REGISTRY_SECURE} \ + --set dockerRegistry.bentoRepositoryName=${DOCKER_REGISTRY_BENTO_REPOSITORY_NAME} \ + --set aws.accessKeyID=${AWS_ACCESS_KEY_ID} \ + --set aws.secretAccessKeyExistingSecretName=${AWS_SECRET_ACCESS_KEY_EXISTING_SECRET_NAME} \ + --set aws.secretAccessKeyExistingSecretKey=${AWS_SECRET_ACCESS_KEY_EXISTING_SECRET_KEY} \ + --set yataiSystem.serviceAccountName=$YATAI_SERVICE_ACCOUNT \ + --version=${VERSION} \ + --devel=${DEVEL} +fi kubectl -n ${namespace} rollout restart deploy/yatai-image-builder echo "โณ waiting for yatai-image-builder to be ready..." -kubectl -n ${namespace} wait --for=condition=available --timeout=600s deploy/yatai-image-builder +kubectl -n ${namespace} wait --for=condition=available --timeout=180s deploy/yatai-image-builder echo "โœ… yatai-image-builder is ready" diff --git a/tests/e2e/e2e_suite_test.go b/tests/e2e/e2e_suite_test.go new file mode 100644 index 0000000..ceb0c18 --- /dev/null +++ b/tests/e2e/e2e_suite_test.go @@ -0,0 +1,16 @@ +package e2e + +import ( + "fmt" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// Run e2e tests using the Ginkgo runner. +func TestE2E(t *testing.T) { + RegisterFailHandler(Fail) + fmt.Fprintf(GinkgoWriter, "Starting yatai-image-builder e2e suite\n") + RunSpecs(t, "yatai-image-builder e2e suite") +} diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go new file mode 100644 index 0000000..1203ba0 --- /dev/null +++ b/tests/e2e/e2e_test.go @@ -0,0 +1,135 @@ +//nolint:wrapcheck,gosec +package e2e + +import ( + "context" + "fmt" + "os" + "os/exec" + "time" + + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client/config" + + commonconsts "github.com/bentoml/yatai-common/consts" + + resourcesclient "github.com/bentoml/yatai-image-builder/generated/resources/clientset/versioned/typed/resources/v1alpha1" + + //nolint:golint + //nolint:revive + . "github.com/onsi/ginkgo/v2" + + //nolint:golint + //nolint:revive + . "github.com/onsi/gomega" + + "github.com/bentoml/yatai-image-builder/tests/utils" +) + +var _ = Describe("yatai-image-builder", Ordered, func() { + AfterAll(func() { + if os.Getenv("DEBUG") == "true" { + return + } + By("Showing image builder pod events") + cmd := exec.Command("kubectl", "-n", "yatai", "describe", "pod", "-l", fmt.Sprintf("%s=true", commonconsts.KubeLabelIsBentoImageBuilder)) + logs, _ := utils.Run(cmd) + fmt.Println(string(logs)) + By("Showing image builder pod bento-downloader container logs") + cmd = exec.Command("kubectl", "-n", "yatai", "logs", "-c", "bento-downloader", "--tail", "200", "-l", fmt.Sprintf("%s=true", commonconsts.KubeLabelIsBentoImageBuilder)) + logs, _ = utils.Run(cmd) + fmt.Println(string(logs)) + By("Showing image builder pod builder container logs") + cmd = exec.Command("kubectl", "-n", "yatai", "logs", "-c", "builder", "--tail", "200", "-l", fmt.Sprintf("%s=true", commonconsts.KubeLabelIsBentoImageBuilder)) + logs, _ = utils.Run(cmd) + fmt.Println(string(logs)) + By("Showing yatai-image-builder events") + cmd = exec.Command("kubectl", "-n", "yatai-image-builder", "describe", "pod", "-l", "app.kubernetes.io/name=yatai-image-builder") + logs, _ = utils.Run(cmd) + fmt.Println(string(logs)) + By("Showing yatai-image-builder logs") + cmd = exec.Command("kubectl", "-n", "yatai-image-builder", "logs", "--tail", "200", "-l", "app.kubernetes.io/name=yatai-image-builder") + logs, _ = utils.Run(cmd) + fmt.Println(string(logs)) + By("Cleaning up BentoRequest resources") + cmd = exec.Command("kubectl", "delete", "-f", "tests/e2e/example.yaml") + _, _ = utils.Run(cmd) + }) + + Context("BentoRequest Operator", func() { + It("Should run successfully", func() { + By("Creating a BentoRequest CR") + cmd := exec.Command("kubectl", "apply", "-f", "tests/e2e/example.yaml") + out, err := utils.Run(cmd) + Expect(err).To(BeNil(), "Failed to create BentoRequest CR: %s", string(out)) + + By("Sleeping for 5 seconds") + time.Sleep(5 * time.Second) + + restConf := config.GetConfigOrDie() + cliset, err := kubernetes.NewForConfig(restConf) + Expect(err).To(BeNil(), "failed to create kubernetes clientset") + + By("Checking the generated image builder pod") + EventuallyWithOffset(1, func() error { + pods, err := cliset.CoreV1().Pods("yatai").List(context.Background(), metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=true", commonconsts.KubeLabelIsBentoImageBuilder), + }) + if err != nil { + return err + } + if len(pods.Items) != 2 { + return fmt.Errorf("expected 2 pod, got %d", len(pods.Items)) + } + pod := pods.Items[0] + if pod.Status.Phase == corev1.PodFailed { + Fail(fmt.Sprintf("pod %s failed", pod.Name)) + } + if pod.Status.Phase != corev1.PodSucceeded { + return fmt.Errorf("pod not finished yet") + } + return nil + }, 10*time.Minute, time.Second).Should(Succeed()) + + bentorequestcli, err := resourcesclient.NewForConfig(restConf) + Expect(err).To(BeNil(), "failed to create bentorequest clientset") + + By("Checking the generated Bento CR") + EventuallyWithOffset(1, func() error { + ctx := context.Background() + + logrus.Infof("Getting BentoRequest CR %s", "test-bento") + bentoRequest, err := bentorequestcli.BentoRequests("yatai").Get(ctx, "test-bento", metav1.GetOptions{}) + Expect(err).To(BeNil(), "failed to get BentoRequest CR test-bento") + + logrus.Infof("Getting Bento CR %s", "test-bento") + bento, err := bentorequestcli.Bentoes("yatai").Get(ctx, "test-bento", metav1.GetOptions{}) + if err != nil { + return err + } + + Expect(bentoRequest.Spec.BentoTag).To(Equal(bento.Spec.Tag), "bentoRequest and bento tag should match") + Expect(bento.Spec.Image).To(Not(BeEmpty()), "bento CR image should not be empty") + + logrus.Infof("Getting BentoRequest CR %s", "test-bento1") + + bentoRequest1, err := bentorequestcli.BentoRequests("yatai").Get(ctx, "test-bento1", metav1.GetOptions{}) + Expect(err).To(BeNil(), "failed to get BentoRequest CR test-bento1") + + logrus.Infof("Getting Bento CR %s", "test-bento1") + bento1, err := bentorequestcli.Bentoes("yatai").Get(ctx, "test-bento1", metav1.GetOptions{}) + if err != nil { + return err + } + + Expect(bentoRequest1.Spec.BentoTag).To(Equal(bento1.Spec.Tag), "bentoRequest1 and bento1 tag should match") + Expect(bento1.Spec.Image).To(Not(BeEmpty()), "bento1 CR image should not be empty") + Expect(bentoRequest1.Spec.Image).To(Equal(bento1.Spec.Image), "bentoRequest1 and bento1 image should match") + return nil + }, time.Minute, time.Second).Should(Succeed()) + }) + }) +}) diff --git a/tests/e2e/example.yaml b/tests/e2e/example.yaml new file mode 100644 index 0000000..37db94e --- /dev/null +++ b/tests/e2e/example.yaml @@ -0,0 +1,20 @@ +apiVersion: resources.yatai.ai/v1alpha1 +kind: BentoRequest +metadata: + name: test-bento + namespace: yatai +spec: + bentoTag: iris_classifier:r4zint4b567i4usu234 + downloadUrl: s3://yetone/bentos/test-bento.bento +--- +apiVersion: resources.yatai.ai/v1alpha1 +kind: BentoRequest +metadata: + name: test-bento1 + namespace: yatai +spec: + image: docker-registry-1.yatai-image-builder.svc.cluster.local:5000/test-custom-image:v1 + ociRegistryInsecure: true + dockerConfigJsonSecretName: regcred + bentoTag: iris_classifier:r4zint4b567i4usu234 + downloadUrl: s3://yetone/bentos/test-bento.bento diff --git a/tests/e2e/installation_test.sh b/tests/e2e/installation_test.sh new file mode 100755 index 0000000..d18cd27 --- /dev/null +++ b/tests/e2e/installation_test.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -xe + +kubectl create ns yatai-system +kubectl create ns yatai-image-builder +kubectl create ns yatai || true + +echo "๐Ÿš€ Creating AWS Secret Access Key..." +kubectl create secret generic aws-secret-access-key --from-literal=AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} --namespace yatai-image-builder +echo "๐Ÿš€ Installing yatai-image-builder..." +YATAI_ENDPOINT='empty' USE_LOCAL_HELM_CHART=true UPGRADE_CRDS=false AWS_SECRET_ACCESS_KEY_EXISTING_SECRET_NAME=aws-secret-access-key AWS_SECRET_ACCESS_KEY_EXISTING_SECRET_KEY=AWS_SECRET_ACCESS_KEY bash ./scripts/quick-install-yatai-image-builder.sh +echo "yatai-image-builder helm release values:" +helm get values yatai-image-builder -n yatai-image-builder + +helm upgrade --install docker-registry-1 twuni/docker-registry -n yatai-image-builder --set secrets.htpasswd='yetone:$2y$05$L7dlu/IGePjzo7C4YbmivOJxHs4jZ9k08D3CAAhxRQJoF62ey93yq' +cat <