Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 16 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: Bug Report
about: Report a bug
labels: kind/bug

---

**What happened**:

**What you expected to happen**:

**How to reproduce it (as minimally and precisely as possible)**:

**Anything else we need to know**:

**Environment**:
10 changes: 10 additions & 0 deletions .github/ISSUE_TEMPLATE/enhancement_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: Enhancement Request
about: Suggest an enhancement
labels: kind/enhancement

---

**What would you like to be added**:

**Why is this needed**:
6 changes: 6 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
**What this PR does / why we need it**:

**Which issue(s) this PR fixes**:
Fixes #

**Special notes for your reviewer**:
33 changes: 33 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: ci
on:
push:
tags:
- v*
branches:
- master
- main
pull_request:

jobs:
build:
runs-on: ubuntu-24.04

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: make tidy
run: |
make tidy
git diff --exit-code
- name: make verify
run: make verify

- name: make test
run: make test
87 changes: 87 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Versioned Release

on:
push:
branches:
- main

permissions:
contents: write # we need this to be able to push tags

jobs:
release_tag:
name: Release version
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.PUSH_KEY }}
fetch-tags: true
fetch-depth: 0

- name: Read and validate VERSION
id: version
run: |
VERSION=$(cat VERSION)
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-dev)?$ ]]; then
echo "Invalid version format in VERSION file: $VERSION"
exit 1
fi
echo "New version: $VERSION"
echo "version=$VERSION" >> $GITHUB_ENV

- name: Skip release if version is a dev version
if: contains(env.version, '-dev')
run: |
echo "Skipping development version release: ${{ env.version }}"
echo "SKIP=true" >> $GITHUB_ENV
exit 0

- name: Check if VERSION is already tagged
id: check_tag
run: |
if git rev-parse "refs/tags/${{ env.version }}" >/dev/null 2>&1; then
echo "Tag ${{ env.version }} already exists. Skipping release."
echo "SKIP=true" >> $GITHUB_ENV
exit 0
fi
echo "Tag ${{ env.version }} doesn't exists. Proceeding with release."

- name: Create Git tag
if: ${{ env.SKIP != 'true' }}
run: |
AUTHOR_NAME=$(git log -1 --pretty=format:'%an')
AUTHOR_EMAIL=$(git log -1 --pretty=format:'%ae')
echo "Tagging as $AUTHOR_NAME <$AUTHOR_EMAIL>"

echo "AUTHOR_NAME=$AUTHOR_NAME" >> $GITHUB_ENV
echo "AUTHOR_EMAIL=$AUTHOR_EMAIL" >> $GITHUB_ENV

git config user.name "$AUTHOR_NAME"
git config user.email "$AUTHOR_EMAIL"

git tag -a "${{ env.version }}" -m "Release ${{ env.version }}"
git push origin "${{ env.version }}"

- name: Create GitHub release
if: ${{ env.SKIP != 'true' }}
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ env.version }}
name: Release ${{ env.version }}
body: "Automated release for version ${{ env.version }}"
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Push dev VERSION
if: ${{ env.SKIP != 'true' }}
run: |
echo "${{ env.version }}-dev" > VERSION
git config user.name "${{ env.AUTHOR_NAME }}"
git config user.email "${{ env.AUTHOR_EMAIL }}"
git add VERSION
git commit -m "Update VERSION to ${{ env.version }}-dev"
git push origin main
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
bin/
Dockerfile.cross

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Kubernetes Generated files - skip generated files, except for vendored files

!vendor/**/zz_generated.*

# editor and IDE paraphernalia
.idea
.vscode
*.swp
*.swo
*~

tmp
go.work*
components/
!**/components/
/**/cover.html
/**/cover.*.html

*.tmp
8 changes: 8 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
run:
concurrency: 4
timeout: 10m

issues:
exclude-files:
- "zz_generated.*\\.go$"
- "tmp/.*"
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Code of Conduct

All members of the project community must abide by the [SAP Open Source Code of Conduct](https://github.com/SAP/.github/blob/main/CODE_OF_CONDUCT.md).
All members of the project community must abide by the [SAP Open Source Code of Conduct](https://github.com/openmcp-project/.github/blob/main/CODE_OF_CONDUCT.md).
Only by respecting each other we can develop a productive, collaborative community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [[email protected]](mailto:[email protected]) (SAP Open Source Program Office). All complaints will be reviewed and investigated promptly and fairly.

Expand Down
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# NOTE: This Dockerfile is used by the pipeline, but not for the 'make image' command, which uses the Dockerfile template in hack/common instead.
# Use distroless as minimal base image to package the component binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot@sha256:6ec5aa99dc335666e79dc64e4a6c8b89c33a543a1967f20d360922a80dd21f02
ARG TARGETOS
ARG TARGETARCH
ARG COMPONENT
WORKDIR /
COPY bin/$COMPONENT-$TARGETOS.$TARGETARCH /$COMPONENT
USER nonroot:nonroot

ENTRYPOINT ["/$COMPONENT"]
165 changes: 165 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
PROJECT_FULL_NAME := mcp-operator
REPO_ROOT := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
EFFECTIVE_VERSION := $(shell $(REPO_ROOT)/hack/common/get-version.sh)

COMMON_MAKEFILE ?= $(REPO_ROOT)/hack/common/Makefile
ifneq (,$(wildcard $(COMMON_MAKEFILE)))
include $(COMMON_MAKEFILE)
endif

# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif

# CONTAINER_TOOL defines the container tool to be used for building images.
# Be aware that the target commands are only tested with Docker which is
# scaffolded by default. However, you might want to replace it to use other
# tools. (i.e. podman)
CONTAINER_TOOL ?= docker

# Setting SHELL to bash allows bash commands to be executed by recipes.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec

COMPONENTS ?= mcp-operator
API_CODE_DIRS := $(REPO_ROOT)/api/constants/... $(REPO_ROOT)/api/errors/... $(REPO_ROOT)/api/install/... $(REPO_ROOT)/api/v1alpha1/... $(REPO_ROOT)/api/core/v1alpha1/...
ROOT_CODE_DIRS := $(REPO_ROOT)/cmd/... $(REPO_ROOT)/internal/... $(REPO_ROOT)/test/...

##@ General

ifndef HELP_TARGET
.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
endif

##@ Development

.PHONY: manifests
manifests: controller-gen ## Generate CustomResourceDefinition objects.
@echo "> Remove existing CRD manifests"
rm -rf config/crd/bases/
rm -rf config/webhook/manifests/
rm -rf api/crds/manifests/
@echo "> Generating CRD Manifests"
@$(CONTROLLER_GEN) crd paths="$(REPO_ROOT)/api/core/v1alpha1/..." output:crd:artifacts:config=config/crd/bases
@$(CONTROLLER_GEN) crd paths="$(REPO_ROOT)/api/core/v1alpha1/..." output:crd:artifacts:config=api/crds/manifests
@$(CONTROLLER_GEN) webhook paths="$(REPO_ROOT)/api/..."

.PHONY: generate
generate: generate-code manifests generate-docs format ## Generates code (DeepCopy stuff, CRDs), documentation index, and runs formatter.

.PHONY: generate-code
generate-code: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. Also fetches external APIs.
@echo "> Fetching External APIs"
@go run $(REPO_ROOT)/hack/external-apis/main.go
@echo "> Generating DeepCopy Methods"
@$(CONTROLLER_GEN) object paths="$(REPO_ROOT)/api/core/v1alpha1/..."

.PHONY: format
format: goimports ## Formats the imports.
@FORMATTER=$(FORMATTER) $(REPO_ROOT)/hack/common/format.sh $(API_CODE_DIRS) $(ROOT_CODE_DIRS)

.PHONY: verify
verify: golangci-lint jq goimports ## Runs linter, 'go vet', and checks if the formatter has been run.
@test "$(SKIP_DOCS_INDEX_CHECK)" = "true" || \
( echo "> Verify documentation index ..." && \
JQ=$(JQ) $(REPO_ROOT)/hack/common/verify-docs-index.sh )
@( echo "> Verifying api module ..." && \
pushd $(REPO_ROOT)/api &>/dev/null && \
go vet $(API_CODE_DIRS) && \
$(LINTER) run -c $(REPO_ROOT)/.golangci.yaml $(API_CODE_DIRS) && \
popd &>/dev/null )
@( echo "> Verifying root module ..." && \
pushd $(REPO_ROOT) &>/dev/null && \
go vet $(ROOT_CODE_DIRS) && \
$(LINTER) run -c $(REPO_ROOT)/.golangci.yaml $(ROOT_CODE_DIRS) && \
popd &>/dev/null )
@test "$(SKIP_FORMATTING_CHECK)" = "true" || \
( echo "> Checking for unformatted files ..." && \
FORMATTER=$(FORMATTER) $(REPO_ROOT)/hack/common/format.sh --verify $(API_CODE_DIRS) $(ROOT_CODE_DIRS) )

.PHONY: test
test: #envtest ## Run tests.
# KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(ROOT_CODE_DIRS) -coverprofile cover.out
@( echo "> Test root module ..." && \
pushd $(REPO_ROOT) &>/dev/null && \
go test $(ROOT_CODE_DIRS) -coverprofile cover.root.out && \
go tool cover --html=cover.root.out -o cover.root.html && \
go tool cover -func cover.root.out | tail -n 1 && \
popd &>/dev/null )

@( echo "> Test api module ..." && \
pushd $(REPO_ROOT)/api &>/dev/null && \
go test $(API_CODE_DIRS) -coverprofile cover.api.out && \
go tool cover --html=cover.api.out -o cover.api.html && \
go tool cover -func cover.api.out | tail -n 1 && \
popd &>/dev/null )

##@ Build Dependencies

## Location to install dependencies to
LOCALBIN ?= $(REPO_ROOT)/bin

# Tool Binaries
ENVTEST ?= $(LOCALBIN)/setup-envtest

# Tool Versions
SETUP_ENVTEST_VERSION ?= release-0.16

ifndef LOCALBIN_TARGET
.PHONY: localbin
localbin:
@test -d $(LOCALBIN) || mkdir -p $(LOCALBIN)
endif

.PHONY: envtest
envtest: localbin ## Download envtest-setup locally if necessary.
@test -s $(LOCALBIN)/setup-envtest && test -s $(LOCALBIN)/setup-envtest_version && cat $(LOCALBIN)/setup-envtest_version | grep -q $(SETUP_ENVTEST_VERSION) || \
( echo "Installing setup-envtest $(SETUP_ENVTEST_VERSION) ..."; \
GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@$(SETUP_ENVTEST_VERSION) && \
echo $(SETUP_ENVTEST_VERSION) > $(LOCALBIN)/setup-envtest_version )

##@ Local Setup

DISABLE_AUTHENTICATION ?= true
DISABLE_AUTHORIZATION ?= true
DISABLE_CLOUDORCHESTRATOR ?= true
DISABLE_MANAGEDCONTROLPLANE ?= false
DISABLE_APISERVER ?= true
DISABLE_LANDSCAPER ?= true
LOCAL_GOARCH ?= $(shell go env GOARCH)

.PHONY: dev-local
dev-local: dev-clean image-build-local dev-cluster load-image helm-install-local ## All-in-one command for creating a fresh local setup.

.PHONY: dev-clean
dev-clean: ## Removes the kind cluster for local setup.
$(KIND) delete cluster --name=$(PROJECT_FULL_NAME)-dev

.PHONY: dev-cluster
dev-cluster: ## Creates a kind cluster for running a local setup.
$(KIND) create cluster --name=$(PROJECT_FULL_NAME)-dev

.PHONY: load-image
load-image: ## Loads the image into the local setup kind cluster.
$(KIND) load docker-image local/mcp-operator:${EFFECTIVE_VERSION}-linux-$(LOCAL_GOARCH) --name=$(PROJECT_FULL_NAME)-dev

.PHONY: helm-install-local
helm-install-local: ## Installs the MCP Operator into the local setup kind cluster by using its helm chart.
helm upgrade --install $(PROJECT_FULL_NAME) charts/$(PROJECT_FULL_NAME)/ --set image.repository=local/mcp-operator --set image.tag=${EFFECTIVE_VERSION}-linux-$(LOCAL_GOARCH) --set image.pullPolicy=Never \
--set authentication.disabled=$(DISABLE_AUTHENTICATION) \
--set authorization.disabled=$(DISABLE_AUTHORIZATION) \
--set cloudOrchestrator.disabled=$(DISABLE_CLOUDORCHESTRATOR) \
--set managedcontrolplane.disabled=$(DISABLE_MANAGEDCONTROLPLANE) \
--set apiserver.disabled=$(DISABLE_APISERVER) \
--set landscaper.disabled=$(DISABLE_LANDSCAPER)

.PHONY: install
install: manifests ## Install CRDs into the K8s cluster specified in ~/.kube/config (or $KUBECONFIG). Usually not required, as the MCP Operator installs the CRDs on its own.
$(KUBECTL) apply -f config/crd/bases

Loading
Loading