diff --git a/Dockerfile b/Dockerfile index aa74435..a039b67 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,22 +12,21 @@ COPY go.sum go.sum RUN go mod download # Copy the go source -COPY cmd/main.go cmd/main.go -COPY api/ api/ -COPY internal/controller/ internal/controller/ +COPY . . # Build # the GOARCH has not a default value to allow the binary be built according to the host where the command # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go +RUN --mount=type=cache,target=/root/.cache/go-build \ + CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} make golangci-build # 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 . +COPY --from=builder /workspace/bin/manager . USER 65532:65532 ENTRYPOINT ["/manager"] diff --git a/Makefile b/Makefile index db36304..1c29c1c 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,10 @@ +COMMIT := $(shell git rev-parse HEAD) +COMMIT_SHORT := $(shell git rev-parse --short HEAD) +DATE := $(shell date +%Y-%m-%d) +BRANCH := $(shell git rev-parse --abbrev-ref HEAD) +VERSION ?= ${BRANCH}-${COMMIT_SHORT} +PKG_LDFLAGS := github.com/prometheus/common/version +LDFLAGS := -s -w -X ${PKG_LDFLAGS}.Version=${VERSION} -X ${PKG_LDFLAGS}.Revision=${COMMIT} -X ${PKG_LDFLAGS}.BuildDate=${DATE} -X ${PKG_LDFLAGS}.Branch=${BRANCH} # Image URL to use all building/pushing image targets IMG ?= controller:latest @@ -82,13 +89,16 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes ##@ Build +.PHONY: golangci-build +golangci-build: ## Build manager binary. + go build -ldflags "${LDFLAGS}" -a -o bin/manager cmd/main.go + .PHONY: build -build: manifests generate fmt vet ## Build manager binary. - go build -o bin/manager cmd/main.go +build: manifests generate fmt vet golangci-build ## Build manager binary. .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. - go run ./cmd/main.go + go run -ldflags "${LDFLAGS}" ./cmd/main.go # If you wish to build the manager image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. diff --git a/cmd/main.go b/cmd/main.go index 684091f..d72fdc5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -23,6 +23,7 @@ import ( // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. + "github.com/prometheus/common/version" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/apimachinery/pkg/runtime" @@ -130,6 +131,7 @@ func main() { } setupLog.Info("starting manager") + setupLog.Info("version", "version", version.Version, "branch", version.Branch, "revision", version.Revision, "builddate", version.BuildDate) if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { setupLog.Error(err, "problem running manager") os.Exit(1) diff --git a/controllers/shim_controller.go b/controllers/shim_controller.go deleted file mode 100644 index 0eae013..0000000 --- a/controllers/shim_controller.go +++ /dev/null @@ -1,200 +0,0 @@ -/* -Copyright 2023. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controllers - -import ( - "context" - "fmt" - "os" - - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/record" - 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" - - "github.com/kwasm/kwasm-operator/api/v1beta1" - runtimev1beta1 "github.com/kwasm/kwasm-operator/api/v1beta1" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// ShimReconciler reconciles a Shim object -type ShimReconciler struct { - client.Client - Scheme *runtime.Scheme - Recorder record.EventRecorder -} - -// Definitions to manage status conditions -const ( - // typeAvailableShim represents the status of the Deployment reconciliation - typeAvailableShim = "Available" - // typeDegradedShim represents the status used when the custom resource is deleted and the finalizer operations are must to occur. - typeDegradedShim = "Degraded" -) - -//+kubebuilder:rbac:groups=runtime.kwasm.sh,resources=shims,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=runtime.kwasm.sh,resources=shims/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=runtime.kwasm.sh,resources=shims/finalizers,verbs=update - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the Shim object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.1/pkg/reconcile -func (r *ShimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) - // Fetch the Shim custom resource - shim := &v1beta1.Shim{} - err := r.Get(ctx, req.NamespacedName, shim) - if err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - nodeList := &corev1.NodeList{} - if err := r.Client.List(ctx, nodeList); err != nil { - log.Log.Error(err, "Failed to get all nodes status") - return ctrl.Result{}, err - } - - for _, node := range nodeList.Items { - - if !matchNode(shim, node) { - continue - } - - // Create a new Kubernetes Job based on the Shim custom resource - job := r.buildJobForShim(shim, node) - - // Check if the Job already exists, if not, create it - found := &batchv1.Job{} - err = r.Get(ctx, types.NamespacedName{ - Name: job.Name, - Namespace: job.Namespace}, found) - if err != nil && client.IgnoreNotFound(err) != nil { - return ctrl.Result{}, err - } - - if err != nil { - // Job does not exist, create it - if err := r.Create(ctx, job); err != nil { - log.Log.Error(err, "Failed to create job") - //return ctrl.Result{}, err - } - - err := r.Get(ctx, req.NamespacedName, shim) - if err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - r.Recorder.Event(shim, corev1.EventTypeNormal, "Created", "something was created") - - if err := r.UpdateConditions(ctx, shim, metav1.Condition{ - Type: typeAvailableShim, - Status: metav1.ConditionUnknown, - Reason: "Reconciling", - Message: "Job created"}); err != nil { - return ctrl.Result{}, err - } - } - } - // Job already exists, do nothing - return ctrl.Result{}, nil -} - -func (r *ShimReconciler) buildJobForShim(shim *v1beta1.Shim, node corev1.Node) *batchv1.Job { - //TODO: check if shim nodeselctor matches node. - - backoffLimit := int32(1) - job := &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s", shim.Name, node.Name), - Namespace: os.Getenv("CONTROLLER_NAMESPACE"), - }, - Spec: batchv1.JobSpec{ - BackoffLimit: &backoffLimit, - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - RestartPolicy: "Never", - NodeName: node.Name, - NodeSelector: shim.Spec.NodeSelector, - Containers: []corev1.Container{ - { - Name: "my-shim-container", - Image: "your-specific-image", // Set your specific image here - // Add other container settings as needed - }, - }, - }, - }, - }, - } - - // Set the owner reference to the Shim custom resource - if err := controllerutil.SetControllerReference(shim, job, r.Scheme); err != nil { - return nil - } - - return job -} - -func (r *ShimReconciler) UpdateConditions(ctx context.Context, shim *runtimev1beta1.Shim, condition metav1.Condition) error { - meta.SetStatusCondition(&shim.Status.Conditions, condition) - if err := r.Update(ctx, shim); err != nil { - log.Log.Error(err, "Failed to update shim status") - return err - } - - if err := r.Get(ctx, types.NamespacedName{Namespace: shim.Namespace, Name: shim.Name}, shim); err != nil { - log.Log.Error(err, "Failed to re-fetch shim") - return err - } - - return nil -} - -func matchNode(shim *v1beta1.Shim, node corev1.Node) bool { - if shim.Spec.NodeSelector == nil { - return true - } - matchNode := false - for selector, selectorValue := range shim.Spec.NodeSelector { - for label, labelValue := range node.Labels { - if label == selector && labelValue == selectorValue { - matchNode = true - break - } - } - } - return matchNode -} - -// SetupWithManager sets up the controller with the Manager. -func (r *ShimReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&runtimev1beta1.Shim{}). - Complete(r) -}