diff --git a/controllers/Dockerfile b/controllers/Dockerfile index 45bd00135..5beef8a17 100644 --- a/controllers/Dockerfile +++ b/controllers/Dockerfile @@ -12,7 +12,6 @@ RUN --mount=type=cache,target=/go/pkg/mod \ COPY model model COPY controllers controllers -COPY kpack-image-builder kpack-image-builder COPY statefulset-runner statefulset-runner COPY tools tools COPY version version diff --git a/controllers/config/config.go b/controllers/config/config.go index df959026d..53b9115ec 100644 --- a/controllers/config/config.go +++ b/controllers/config/config.go @@ -10,7 +10,6 @@ import ( type ControllerConfig struct { // components - IncludeKpackImageBuilder bool `yaml:"includeKpackImageBuilder"` IncludeStatefulsetRunner bool `yaml:"includeStatefulsetRunner"` // core controllers @@ -28,13 +27,7 @@ type ControllerConfig struct { LogLevel zapcore.Level `yaml:"logLevel"` SpaceFinalizerAppDeletionTimeout *int32 `yaml:"spaceFinalizerAppDeletionTimeout"` - // kpack-image-builder - ClusterBuilderName string `yaml:"clusterBuilderName"` - BuilderServiceAccount string `yaml:"builderServiceAccount"` - BuilderReadinessTimeout string `yaml:"builderReadinessTimeout"` - ContainerRepositoryPrefix string `yaml:"containerRepositoryPrefix"` - ContainerRegistryType string `yaml:"containerRegistryType"` - Networking Networking `yaml:"networking"` + Networking Networking `yaml:"networking"` ExperimentalManagedServicesEnabled bool `yaml:"experimentalManagedServicesEnabled"` TrustInsecureServiceBrokers bool `yaml:"trustInsecureServiceBrokers"` @@ -102,7 +95,3 @@ func (c ControllerConfig) ParseTaskTTL() (time.Duration, error) { return tools.ParseDuration(c.TaskTTL) } - -func (c ControllerConfig) ParseBuilderReadinessTimeout() (time.Duration, error) { - return tools.ParseDuration(c.BuilderReadinessTimeout) -} diff --git a/controllers/main.go b/controllers/main.go index 141115360..7bd7a957e 100644 --- a/controllers/main.go +++ b/controllers/main.go @@ -60,12 +60,9 @@ import ( packageswebhook "code.cloudfoundry.org/korifi/controllers/webhooks/workloads/packages" spaceswebhook "code.cloudfoundry.org/korifi/controllers/webhooks/workloads/spaces" taskswebhook "code.cloudfoundry.org/korifi/controllers/webhooks/workloads/tasks" - "code.cloudfoundry.org/korifi/kpack-image-builder/controllers" - kpackimagebuilderfinalizer "code.cloudfoundry.org/korifi/kpack-image-builder/controllers/webhooks/finalizer" statefulsetcontrollers "code.cloudfoundry.org/korifi/statefulset-runner/controllers" "code.cloudfoundry.org/korifi/tools" "code.cloudfoundry.org/korifi/tools/image" - "code.cloudfoundry.org/korifi/tools/registry" "code.cloudfoundry.org/korifi/version" buildv1alpha2 "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" @@ -336,49 +333,6 @@ func main() { os.Exit(1) } - if controllerConfig.IncludeKpackImageBuilder { - var builderReadinessTimeout time.Duration - builderReadinessTimeout, err = controllerConfig.ParseBuilderReadinessTimeout() - if err != nil { - setupLog.Error(err, "error parsing builderReadinessTimeout") - os.Exit(1) - } - if err = controllers.NewBuildWorkloadReconciler( - mgr.GetClient(), - mgr.GetScheme(), - controllersLog, - controllerConfig, - imageClient, - controllerConfig.ContainerRepositoryPrefix, - registry.NewRepositoryCreator(controllerConfig.ContainerRegistryType), - builderReadinessTimeout, - ).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "BuildWorkload") - os.Exit(1) - } - - if err = controllers.NewBuilderInfoReconciler( - mgr.GetClient(), - mgr.GetScheme(), - controllersLog, - controllerConfig.ClusterBuilderName, - controllerConfig.CFRootNamespace, - ).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "BuilderInfo") - os.Exit(1) - } - - if err = controllers.NewKpackBuildController( - mgr.GetClient(), - controllersLog, - imageClient, - controllerConfig.BuilderServiceAccount, - ).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "KpackBuild") - os.Exit(1) - } - } - if controllerConfig.IncludeStatefulsetRunner { if err = statefulsetcontrollers.NewAppWorkloadReconciler( mgr.GetClient(), @@ -532,10 +486,6 @@ func main() { os.Exit(1) } - if controllerConfig.IncludeKpackImageBuilder { - kpackimagebuilderfinalizer.NewKpackImageBuilderFinalizerWebhook().SetupWebhookWithManager(mgr) - } - if err = mgr.AddReadyzCheck("readyz", mgr.GetWebhookServer().StartedChecker()); err != nil { setupLog.Error(err, "unable to set up ready check") os.Exit(1) diff --git a/controllers/remote-debug/Dockerfile b/controllers/remote-debug/Dockerfile index f21d2884a..4204f44f8 100644 --- a/controllers/remote-debug/Dockerfile +++ b/controllers/remote-debug/Dockerfile @@ -12,8 +12,6 @@ RUN --mount=type=cache,target=/go/pkg/mod \ COPY model model COPY controllers controllers -COPY kpack-image-builder kpack-image-builder -COPY job-task-runner job-task-runner COPY statefulset-runner statefulset-runner COPY tools tools COPY version version diff --git a/helm/korifi/controllers/configmap.yaml b/helm/korifi/controllers/configmap.yaml index 13adabd66..c4ea0d61a 100644 --- a/helm/korifi/controllers/configmap.yaml +++ b/helm/korifi/controllers/configmap.yaml @@ -5,7 +5,6 @@ metadata: namespace: {{ .Release.Namespace }} data: config.yaml: |- - includeKpackImageBuilder: {{ .Values.kpackImageBuilder.include }} includeStatefulsetRunner: {{ .Values.statefulsetRunner.include }} builderName: {{ .Values.reconcilers.build }} runnerName: {{ .Values.reconcilers.run }} @@ -37,19 +36,6 @@ data: maxRetainedPackagesPerApp: {{ .Values.controllers.maxRetainedPackagesPerApp }} maxRetainedBuildsPerApp: {{ .Values.controllers.maxRetainedBuildsPerApp }} logLevel: {{ .Values.logLevel }} - {{- if .Values.kpackImageBuilder.include }} - clusterBuilderName: {{ .Values.kpackImageBuilder.clusterBuilderName | default "cf-kpack-cluster-builder" }} - builderReadinessTimeout: {{ required "builderReadinessTimeout is required" .Values.kpackImageBuilder.builderReadinessTimeout }} - containerRepositoryPrefix: {{ .Values.containerRepositoryPrefix | quote }} - builderServiceAccount: kpack-service-account - cfStagingResources: - buildCacheMB: {{ .Values.stagingRequirements.buildCacheMB }} - diskMB: {{ .Values.stagingRequirements.diskMB }} - memoryMB: {{ .Values.stagingRequirements.memoryMB }} - {{- if .Values.eksContainerRegistryRoleARN }} - containerRegistryType: "ECR" - {{- end }} - {{- end }} networking: gatewayNamespace: {{ .Release.Namespace }}-gateway gatewayName: korifi diff --git a/helm/korifi/controllers/rbac.yaml b/helm/korifi/controllers/rbac.yaml index 75500ef3a..8071736ed 100644 --- a/helm/korifi/controllers/rbac.yaml +++ b/helm/korifi/controllers/rbac.yaml @@ -80,38 +80,6 @@ subjects: name: korifi-controllers-controller-manager namespace: {{ .Release.Namespace }} -{{- if .Values.jobTaskRunner.include }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: korifi-job-task-runner-manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: korifi-job-task-runner-taskworkload-manager-role -subjects: -- kind: ServiceAccount - name: korifi-controllers-controller-manager - namespace: {{ .Release.Namespace }} -{{- end }} - -{{- if .Values.kpackImageBuilder.include }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: korifi-kpack-build-manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: korifi-kpack-build-manager-role -subjects: -- kind: ServiceAccount - name: korifi-controllers-controller-manager - namespace: {{ .Release.Namespace }} -{{- end }} - {{- if .Values.statefulsetRunner.include }} --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/helm/korifi/kpack-image-builder/cert.yaml b/helm/korifi/kpack-image-builder/cert.yaml new file mode 100644 index 000000000..2dba93cfd --- /dev/null +++ b/helm/korifi/kpack-image-builder/cert.yaml @@ -0,0 +1,13 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: kpack-image-builder-serving-cert + namespace: {{ .Release.Namespace }} +spec: + dnsNames: + - kpack-image-builder-webhook-service.{{ .Release.Namespace }}.svc + - kpack-image-builder-webhook-service.{{ .Release.Namespace }}.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: kpack-image-builder-webhook-cert diff --git a/helm/korifi/kpack-image-builder/configmap.yaml b/helm/korifi/kpack-image-builder/configmap.yaml new file mode 100644 index 000000000..3592153b5 --- /dev/null +++ b/helm/korifi/kpack-image-builder/configmap.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: kpack-image-builder-config + namespace: {{ .Release.Namespace }} +data: + config.yaml: |- + cfRootNamespace: {{ .Values.rootNamespace }} + clusterBuilderName: {{ .Values.kpackImageBuilder.clusterBuilderName | default "cf-kpack-cluster-builder" }} + builderReadinessTimeout: {{ required "builderReadinessTimeout is required" .Values.kpackImageBuilder.builderReadinessTimeout }} + containerRepositoryPrefix: {{ .Values.containerRepositoryPrefix | quote }} + builderServiceAccount: kpack-service-account + cfStagingResources: + buildCacheMB: {{ .Values.stagingRequirements.buildCacheMB }} + diskMB: {{ .Values.stagingRequirements.diskMB }} + memoryMB: {{ .Values.stagingRequirements.memoryMB }} + {{- if .Values.eksContainerRegistryRoleARN }} + containerRegistryType: "ECR" + {{- end }} diff --git a/helm/korifi/kpack-image-builder/deployment.yaml b/helm/korifi/kpack-image-builder/deployment.yaml new file mode 100644 index 000000000..42f34960b --- /dev/null +++ b/helm/korifi/kpack-image-builder/deployment.yaml @@ -0,0 +1,95 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: kpack-image-builder + name: kpack-image-builder-controller-manager + namespace: {{ .Release.Namespace }} +spec: + replicas: {{ .Values.kpackImageBuilder.replicas | default 1}} + selector: + matchLabels: + app: kpack-image-builder + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + prometheus.io/path: /metrics + prometheus.io/port: "8080" + prometheus.io/scrape: "true" + checksum/config: {{ tpl ($.Files.Get "controllers/configmap.yaml") $ | sha256sum }} + labels: + app: kpack-image-builder + spec: + containers: + - name: manager + image: {{ .Values.kpackImageBuilder.image }} +{{- if .Values.debug }} + command: + - "/dlv" + args: + - "--listen=:40000" + - "--headless=true" + - "--api-version=2" + - "exec" + - "/manager" + - "--continue" + - "--accept-multiclient" + - "--" + - "--health-probe-bind-address=:8081" + - "--leader-elect" + - "--config=/etc/kpack-image-builder-config" +{{- else }} + args: + - --health-probe-bind-address=:8081 + - --leader-elect + - --config=/etc/kpack-image-builder-config +{{- end }} + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + - containerPort: 8080 + name: metrics + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + {{- .Values.jobTaskRunner.resources | toYaml | nindent 10 }} + {{- include "korifi.securityContext" . | indent 8 }} + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + - mountPath: /etc/kpack-image-builder-config + name: kpack-image-builder-config + readOnly: true + {{- include "korifi.podSecurityContext" . | indent 6 }} + serviceAccountName: kpack-image-builder-controller-manager +{{- if .Values.kpackImageBuilder.nodeSelector }} + nodeSelector: + {{ toYaml .Values.kpackImageBuilder.nodeSelector | indent 8 }} +{{- end }} +{{- if .Values.kpackImageBuilder.tolerations }} + tolerations: + {{- toYaml .Values.kpackImageBuilder.tolerations | nindent 8 }} +{{- end }} + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: kpack-image-builder-webhook-cert + - configMap: + name: kpack-image-builder-config + name: kpack-image-builder-config diff --git a/helm/korifi/kpack-image-builder/manifests.yaml b/helm/korifi/kpack-image-builder/manifests.yaml index 9261c5af1..2b17cbb09 100644 --- a/helm/korifi/kpack-image-builder/manifests.yaml +++ b/helm/korifi/kpack-image-builder/manifests.yaml @@ -4,14 +4,14 @@ kind: MutatingWebhookConfiguration metadata: name: korifi-kpack-image-builder-mutating-webhook-configuration annotations: - cert-manager.io/inject-ca-from: '{{ .Release.Namespace }}/korifi-controllers-serving-cert' + cert-manager.io/inject-ca-from: '{{ .Release.Namespace }}/kpack-image-builder-serving-cert' webhooks: - admissionReviewVersions: - v1 - v1beta1 clientConfig: service: - name: korifi-controllers-webhook-service + name: kpack-image-builder-webhook-service namespace: '{{ .Release.Namespace }}' path: /mutate-korifi-cloudfoundry-org-v1alpha1-kpack-image-builder-finalizer failurePolicy: Fail diff --git a/helm/korifi/kpack-image-builder/rbac.yaml b/helm/korifi/kpack-image-builder/rbac.yaml new file mode 100644 index 000000000..ebcddc839 --- /dev/null +++ b/helm/korifi/kpack-image-builder/rbac.yaml @@ -0,0 +1,41 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kpack-image-builder-controller-manager + namespace: {{ .Release.Namespace }} + {{- if .Values.eksContainerRegistryRoleARN }} + annotations: + eks.amazonaws.com/role-arn: {{ .Values.eksContainerRegistryRoleARN }} + {{- end }} +imagePullSecrets: +{{- range .Values.systemImagePullSecrets }} +- name: {{ . | quote }} +{{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: kpack-image-builder-leader-election-rolebinding + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: korifi-controllers-leader-election-role +subjects: +- kind: ServiceAccount + name: kpack-image-builder-controller-manager + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kpack-image-builder-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: korifi-kpack-build-manager-role +subjects: +- kind: ServiceAccount + name: kpack-image-builder-controller-manager + namespace: {{ .Release.Namespace }} diff --git a/helm/korifi/kpack-image-builder/service.yaml b/helm/korifi/kpack-image-builder/service.yaml new file mode 100644 index 000000000..ac41475ba --- /dev/null +++ b/helm/korifi/kpack-image-builder/service.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: Service +metadata: + name: kpack-image-builder-webhook-service + namespace: {{ .Release.Namespace }} +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: kpack-image-builder +--- +{{- if .Values.debug }} +apiVersion: v1 +kind: Service +metadata: + name: kpack-image-builder-debug-port + namespace: {{ .Release.Namespace }} +spec: + ports: + - name: debug-30054 + nodePort: 30054 + port: 30054 + protocol: TCP + targetPort: 40000 + selector: + app: kpack-image-builder + type: NodePort +{{- end }} diff --git a/helm/korifi/values.yaml b/helm/korifi/values.yaml index 935fce330..a4f60ae97 100644 --- a/helm/korifi/values.yaml +++ b/helm/korifi/values.yaml @@ -93,6 +93,7 @@ controllers: kpackImageBuilder: include: true + image: cloudfoundry/kpack-image-builder:latest replicas: 1 resources: limits: diff --git a/kpack-image-builder/Dockerfile b/kpack-image-builder/Dockerfile new file mode 100644 index 000000000..6a2942f33 --- /dev/null +++ b/kpack-image-builder/Dockerfile @@ -0,0 +1,32 @@ +# syntax = docker/dockerfile:experimental +FROM golang:1.23 as builder + +ARG version=dev + +WORKDIR /workspace + +COPY go.mod go.sum ./ + +RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download + +COPY api api +COPY controllers controllers +COPY kpack-image-builder kpack-image-builder +COPY model model +COPY tools tools +COPY version version + +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 GOOS=linux go build -ldflags "-X code.cloudfoundry.org/korifi/version.Version=${version}" -o manager kpack-image-builder/main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot + +WORKDIR / +COPY --from=builder /workspace/manager . +USER 65532:65532 + +ENTRYPOINT ["/manager"] diff --git a/kpack-image-builder/Makefile b/kpack-image-builder/Makefile index 3cb601339..fc0043cc7 100644 --- a/kpack-image-builder/Makefile +++ b/kpack-image-builder/Makefile @@ -42,10 +42,10 @@ manifests: bin/controller-gen output:rbac:artifacts:config=../helm/korifi/kpack-image-builder \ output:webhook:artifacts:config=../helm/korifi/kpack-image-builder - yq -i 'with(.metadata; .annotations["cert-manager.io/inject-ca-from"]="{{ .Release.Namespace }}/korifi-controllers-serving-cert")' $(webhooks-file) + yq -i 'with(.metadata; .annotations["cert-manager.io/inject-ca-from"]="{{ .Release.Namespace }}/kpack-image-builder-serving-cert")' $(webhooks-file) yq -i 'with(.metadata; .name="korifi-kpack-image-builder-" + .name)' $(webhooks-file) yq -i 'with(.webhooks[]; .clientConfig.service.namespace="{{ .Release.Namespace }}")' $(webhooks-file) - yq -i 'with(.webhooks[]; .clientConfig.service.name="korifi-controllers-" + .clientConfig.service.name)' $(webhooks-file) + yq -i 'with(.webhooks[]; .clientConfig.service.name="kpack-image-builder-" + .clientConfig.service.name)' $(webhooks-file) diff --git a/kpack-image-builder/controllers/buildworkload_controller.go b/kpack-image-builder/controllers/buildworkload_controller.go index 475b23896..e4dbe72aa 100644 --- a/kpack-image-builder/controllers/buildworkload_controller.go +++ b/kpack-image-builder/controllers/buildworkload_controller.go @@ -74,39 +74,43 @@ type RepositoryCreator interface { CreateRepository(ctx context.Context, name string) error } +type Config struct { + CFRootNamespace string `yaml:"cfRootNamespace"` + CFStagingResources config.CFStagingResources `yaml:"cfStagingResources"` + ClusterBuilderName string `yaml:"clusterBuilderName"` + BuilderServiceAccount string `yaml:"builderServiceAccount"` + BuilderReadinessTimeout time.Duration `yaml:"builderReadinessTimeout"` + ContainerRepositoryPrefix string `yaml:"containerRepositoryPrefix"` + ContainerRegistryType string `yaml:"containerRegistryType"` +} + func NewBuildWorkloadReconciler( c client.Client, scheme *runtime.Scheme, log logr.Logger, - config *config.ControllerConfig, + config *Config, imageConfigGetter ImageConfigGetter, - imageRepoPrefix string, imageRepoCreator RepositoryCreator, - builderReadinessTimeout time.Duration, ) *k8s.PatchingReconciler[korifiv1alpha1.BuildWorkload, *korifiv1alpha1.BuildWorkload] { buildWorkloadReconciler := BuildWorkloadReconciler{ - k8sClient: c, - scheme: scheme, - log: log, - controllerConfig: config, - imageConfigGetter: imageConfigGetter, - imageRepoPrefix: imageRepoPrefix, - imageRepoCreator: imageRepoCreator, - builderReadinessTimeout: builderReadinessTimeout, + k8sClient: c, + scheme: scheme, + log: log, + controllerConfig: config, + imageConfigGetter: imageConfigGetter, + imageRepoCreator: imageRepoCreator, } return k8s.NewPatchingReconciler[korifiv1alpha1.BuildWorkload, *korifiv1alpha1.BuildWorkload](log, c, &buildWorkloadReconciler) } // BuildWorkloadReconciler reconciles a BuildWorkload object type BuildWorkloadReconciler struct { - k8sClient client.Client - scheme *runtime.Scheme - log logr.Logger - controllerConfig *config.ControllerConfig - imageConfigGetter ImageConfigGetter - imageRepoPrefix string - imageRepoCreator RepositoryCreator - builderReadinessTimeout time.Duration + k8sClient client.Client + scheme *runtime.Scheme + log logr.Logger + controllerConfig *Config + imageConfigGetter ImageConfigGetter + imageRepoCreator RepositoryCreator } func (r *BuildWorkloadReconciler) SetupWithManager(mgr ctrl.Manager) *builder.Builder { @@ -210,7 +214,7 @@ func (r *BuildWorkloadReconciler) ReconcileResource(ctx context.Context, buildWo } if !builderReadyCondition.IsTrue() { - if time.Since(buildWorkload.CreationTimestamp.Time) < r.builderReadinessTimeout { + if time.Since(buildWorkload.CreationTimestamp.Time) < r.controllerConfig.BuilderReadinessTimeout { log.Info("waiting for builder to be ready") return ctrl.Result{RequeueAfter: time.Second}, nil } @@ -440,7 +444,7 @@ func (r *BuildWorkloadReconciler) ensureKpackBuilderForBuildpacks(ctx context.Co } builderName := ComputeBuilderName(buildWorkload.Spec.Buildpacks) - builderRepo := fmt.Sprintf("%sbuilders-%s", r.imageRepoPrefix, builderName) + builderRepo := fmt.Sprintf("%sbuilders-%s", r.controllerConfig.ContainerRepositoryPrefix, builderName) err = r.imageRepoCreator.CreateRepository(ctx, builderRepo) if err != nil { log.Info("failed creating builder repo", "reason", err) @@ -930,5 +934,5 @@ func (r *BuildWorkloadReconciler) hasRemainingBuilds(ctx context.Context, buildW } func (r *BuildWorkloadReconciler) repositoryRef(appGUID string) string { - return r.imageRepoPrefix + appGUID + "-droplets" + return r.controllerConfig.ContainerRepositoryPrefix + appGUID + "-droplets" } diff --git a/kpack-image-builder/controllers/suite_test.go b/kpack-image-builder/controllers/suite_test.go index e42c7ddc5..af2cf4ab6 100644 --- a/kpack-image-builder/controllers/suite_test.go +++ b/kpack-image-builder/controllers/suite_test.go @@ -105,11 +105,12 @@ var _ = BeforeSuite(func() { finalizer.NewKpackImageBuilderFinalizerWebhook().SetupWebhookWithManager(k8sManager) - controllerConfig := &config.ControllerConfig{ + controllerConfig := &controllers.Config{ CFRootNamespace: PrefixedGUID("cf"), ClusterBuilderName: "cf-kpack-builder", - ContainerRepositoryPrefix: "image/registry/tag", BuilderServiceAccount: "builder-service-account", + BuilderReadinessTimeout: 4 * time.Second, + ContainerRepositoryPrefix: "my.repository/my-prefix/", CFStagingResources: config.CFStagingResources{ BuildCacheMB: 1024, DiskMB: 2048, @@ -125,9 +126,7 @@ var _ = BeforeSuite(func() { ctrl.Log.WithName("kpack-image-builder").WithName("BuildWorkload"), controllerConfig, fakeImageConfigGetter, - "my.repository/my-prefix/", imageRepoCreator, - 4*time.Second, ) err = buildWorkloadReconciler.SetupWithManager(k8sManager) Expect(err).NotTo(HaveOccurred()) diff --git a/kpack-image-builder/main.go b/kpack-image-builder/main.go new file mode 100644 index 000000000..1f8b1bf02 --- /dev/null +++ b/kpack-image-builder/main.go @@ -0,0 +1,160 @@ +package main + +import ( + "flag" + "fmt" + "os" + + korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" + "code.cloudfoundry.org/korifi/kpack-image-builder/controllers" + kpackimagebuilderfinalizer "code.cloudfoundry.org/korifi/kpack-image-builder/controllers/webhooks/finalizer" + "code.cloudfoundry.org/korifi/tools" + "code.cloudfoundry.org/korifi/tools/image" + "code.cloudfoundry.org/korifi/tools/registry" + buildv1alpha2 "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + "go.uber.org/zap/zapcore" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + k8sclient "k8s.io/client-go/kubernetes" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/manager" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(korifiv1alpha1.AddToScheme(scheme)) + utilruntime.Must(buildv1alpha2.AddToScheme(scheme)) +} + +func main() { + var ( + metricsAddr string + enableLeaderElection bool + probeAddr string + configPath string + ) + + flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.StringVar(&configPath, "config", "", "") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.Parse() + + logger, _, err := tools.NewZapLogger(zapcore.InfoLevel) + if err != nil { + setupLog.Error(err, "unable to set up zap logger") + os.Exit(1) + } + + ctrl.SetLogger(logger) + klog.SetLogger(ctrl.Log) + + conf := ctrl.GetConfigOrDie() + mgr, err := ctrl.NewManager(conf, ctrl.Options{ + Scheme: scheme, + WebhookServer: webhook.NewServer(webhook.Options{ + Port: 9443, + }), + Metrics: metricsserver.Options{ + BindAddress: metricsAddr, + }, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "13w500bs.cloudfoundry.org", + }) + if err != nil { + setupLog.Error(err, "unable to initialize manager") + os.Exit(1) + } + + if err = setupControllers(mgr, conf, configPath); err != nil { + setupLog.Error(err, "unable to set up controllers") + os.Exit(1) + } + + if err = setupWebhooks(mgr); err != nil { + setupLog.Error(err, "unable to set up webhooks") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} + +func setupControllers(mgr manager.Manager, restConf *rest.Config, configPath string) error { + controllersLog := ctrl.Log.WithName("controllers") + imageClientSet, err := k8sclient.NewForConfig(restConf) + if err != nil { + return fmt.Errorf("could not create k8s client: %v", err) + } + + controllerConfig := &controllers.Config{} + err = tools.LoadConfigInto(controllerConfig, configPath) + if err != nil { + return fmt.Errorf("config could not be read: %v", err) + } + + imageClient := image.NewClient(imageClientSet) + if err = controllers.NewBuildWorkloadReconciler( + mgr.GetClient(), + mgr.GetScheme(), + controllersLog, + controllerConfig, + imageClient, + registry.NewRepositoryCreator(controllerConfig.ContainerRegistryType), + ).SetupWithManager(mgr); err != nil { + return fmt.Errorf("unable to create BuildWorkload controller: %v", err) + } + + if err = controllers.NewBuilderInfoReconciler( + mgr.GetClient(), + mgr.GetScheme(), + controllersLog, + controllerConfig.ClusterBuilderName, + controllerConfig.CFRootNamespace, + ).SetupWithManager(mgr); err != nil { + return fmt.Errorf("unable to create BuilderInfo controller: %v", err) + } + + if err = controllers.NewKpackBuildController( + mgr.GetClient(), + controllersLog, + imageClient, + controllerConfig.BuilderServiceAccount, + ).SetupWithManager(mgr); err != nil { + return fmt.Errorf("unable to create KpackBuild controller: %v", err) + } + + return nil +} + +func setupWebhooks(mgr manager.Manager) error { + kpackimagebuilderfinalizer.NewKpackImageBuilderFinalizerWebhook().SetupWebhookWithManager(mgr) + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + return fmt.Errorf("unable to set up health check: %v", err) + } + + if err := mgr.AddReadyzCheck("readyz", mgr.GetWebhookServer().StartedChecker()); err != nil { + return fmt.Errorf("unable to set up ready check: %v", err) + } + + return nil +} diff --git a/kpack-image-builder/remote-debug/Dockerfile b/kpack-image-builder/remote-debug/Dockerfile new file mode 100644 index 000000000..2e2e153a8 --- /dev/null +++ b/kpack-image-builder/remote-debug/Dockerfile @@ -0,0 +1,35 @@ +# syntax = docker/dockerfile:experimental +FROM golang:1.23 as builder + +ARG version=dev + +WORKDIR /workspace + +COPY go.mod go.sum ./ + +RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download + +COPY api api +COPY controllers controllers +COPY kpack-image-builder kpack-image-builder +COPY model model +COPY tools tools +COPY version version + +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 GOOS=linux go build -ldflags "-X code.cloudfoundry.org/korifi/version.Version=${version}" -gcflags=all="-N -l" -o manager kpack-image-builder/main.go + +# Get Delve from a GOPATH not from a Go Modules project +WORKDIR /go/src/ +RUN go install github.com/go-delve/delve/cmd/dlv@latest + +FROM ubuntu + +WORKDIR / +COPY --from=builder /workspace/manager . +COPY --from=builder /go/bin/dlv . +EXPOSE 8080 8081 9443 40000 + +CMD ["/dlv", "--listen=:40000", "--headless=true", "--api-version=2", "exec", "/manager", "--continue", "--accept-multiclient"] diff --git a/scripts/assets/korifi-debug-kbld.yml b/scripts/assets/korifi-debug-kbld.yml index ef6cb7425..cf19ef1d8 100644 --- a/scripts/assets/korifi-debug-kbld.yml +++ b/scripts/assets/korifi-debug-kbld.yml @@ -14,6 +14,12 @@ sources: buildx: file: controllers/remote-debug/Dockerfile +- image: cloudfoundry/kpack-image-builder:latest + path: . + docker: + buildx: + file: kpack-image-builder/remote-debug/Dockerfile + - image: cloudfoundry/job-task-runner:latest path: . docker: diff --git a/scripts/assets/korifi-kbld.yml b/scripts/assets/korifi-kbld.yml index 06132be00..359f62d3f 100644 --- a/scripts/assets/korifi-kbld.yml +++ b/scripts/assets/korifi-kbld.yml @@ -14,6 +14,12 @@ sources: buildx: file: controllers/Dockerfile +- image: cloudfoundry/kpack-image-builder:latest + path: . + docker: + buildx: + file: kpack-image-builder/Dockerfile + - image: cloudfoundry/job-task-runner:latest path: . docker: