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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .dockerignore

This file was deleted.

16 changes: 16 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ jobs:
git tag -a "${{ env.version }}" -m "Release ${{ env.version }}"
git push origin "${{ env.version }}"
- name: Create Git tag for api submodule
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 "api/${{ env.version }}" -m "Release ${{ env.version }}"
git push origin "api/${{ env.version }}"
- name: Build Changelog
id: github_release
uses: mikepenz/release-changelog-builder-action@v5
Expand Down
5 changes: 0 additions & 5 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ issues:
# restore some of the defaults
# (fill in the rest as needed)
exclude-rules:
- path: "api/*"
linters:
- lll
- path: "internal/*"
linters:
- dupl
- lll
linters:
disable-all: true
enable:
Expand All @@ -30,7 +26,6 @@ linters:
- gosimple
- govet
- ineffassign
- lll
- misspell
- nakedret
- prealloc
Expand Down
15 changes: 2 additions & 13 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
@@ -1,26 +1,15 @@
version: 3

vars:
NESTED_MODULES: api
API_DIRS: '{{.ROOT_DIR}}/api/provider/v1alpha1/...'
MANIFEST_OUT: '{{.ROOT_DIR}}/api/crds/manifests'
CODE_DIRS: '{{.ROOT_DIR}}/cmd/... {{.ROOT_DIR}}/internal/... {{.ROOT_DIR}}/test/... {{.ROOT_DIR}}/api/provider/v1alpha1/...'
COMPONENTS: 'openmcp-operator'
REPO_URL: 'https://github.com/openmcp-project/openmcp-operator'
GENERATE_DOCS_INDEX: "true"
CHART_COMPONENTS: ""
LINTER_VERSION: "v1.64.4"

includes:
shared:
taskfile: hack/common/Taskfile_controller.yaml
flatten: true
excludes: [] # put task names in here which are overwritten in this file
vars:
NESTED_MODULES: api
API_DIRS: '{{.ROOT_DIR}}/api/provider/v1alpha1/...'
API_DIRS: '{{.ROOT_DIR}}/api/provider/v1alpha1/... {{.ROOT_DIR}}/api/clusters/v1alpha1/...'
MANIFEST_OUT: '{{.ROOT_DIR}}/api/crds/manifests'
CODE_DIRS: '{{.ROOT_DIR}}/cmd/... {{.ROOT_DIR}}/internal/... {{.ROOT_DIR}}/test/... {{.ROOT_DIR}}/api/provider/v1alpha1/...'
CODE_DIRS: '{{.ROOT_DIR}}/cmd/... {{.ROOT_DIR}}/internal/... {{.ROOT_DIR}}/test/... {{.ROOT_DIR}}/api/provider/v1alpha1/... {{.ROOT_DIR}}/api/clusters/v1alpha1/...'
COMPONENTS: 'openmcp-operator'
REPO_URL: 'https://github.com/openmcp-project/openmcp-operator'
GENERATE_DOCS_INDEX: "true"
Expand Down
64 changes: 64 additions & 0 deletions api/clusters/v1alpha1/accessrequest_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package v1alpha1

import (
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type AccessRequestSpec struct {
// ClusterRef is the reference to the Cluster for which access is requested.
// Exactly one of clusterRef or requestRef must be set.
// This value is immutable.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="clusterRef is immutable"
ClusterRef NamespacedObjectReference `json:"clusterRef"`

// Permissions are the requested permissions.
Permissions []PermissionsRequest `json:"permissions"`
}

type PermissionsRequest struct {
// Namespace is the namespace for which the permissions are requested.
// If empty, this will result in a ClusterRole, otherwise in a Role in the respective namespace.
// Note that for a Role, the namespace needs to either exist or a permission to create it must be included in the requested permissions (it will be created automatically then), otherwise the request will be rejected.
// +optional
Namespace string `json:"namespace,omitempty"`

// Rules are the requested RBAC rules.
Rules []rbacv1.PolicyRule `json:"rules"`
}

// AccessRequestStatus defines the observed state of AccessRequest
type AccessRequestStatus struct {
CommonStatus `json:",inline"`

// Phase is the current phase of the request.
Phase RequestPhase `json:"phase"`

// TODO: expose actual access information
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:metadata:labels="openmcp.cloud/cluster=onboarding"

// AccessRequest is the Schema for the accessrequests API
type AccessRequest struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec AccessRequestSpec `json:"spec,omitempty"`
Status AccessRequestStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// AccessRequestList contains a list of AccessRequest
type AccessRequestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []AccessRequest `json:"items"`
}

func init() {
SchemeBuilder.Register(&AccessRequest{}, &AccessRequestList{})
}
143 changes: 143 additions & 0 deletions api/clusters/v1alpha1/cluster_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package v1alpha1

import (
"encoding/json"
"strings"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

// ClusterSpec defines the desired state of Cluster
type ClusterSpec struct {
// Profile is a reference to the cluster provider.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="profile is immutable"
Profile string `json:"profile"`

// ClusterConfigRef is a reference to a cluster configuration.
// +optional
ClusterConfigRef *ClusterConfigRef `json:"clusterConfigRef,omitempty"`

// Kubernetes configuration for the cluster.
Kubernetes K8sConfiguration `json:"kubernetes,omitempty"`

// Purposes lists the purposes this cluster is intended for.
// +kubebuilder:validation:MinItems=1
Purposes []string `json:"purposes,omitempty"`

// Tenancy is the tenancy model of the cluster.
// +kubebuilder:validation:Enum=Exclusive;Shared
Tenancy Tenancy `json:"tenancy"`
}

// ClusterConfigRef is a reference to a cluster configuration.
type ClusterConfigRef struct {
// APIGroup is the group for the resource being referenced.
// +kubebuilder:validation:MinLength=1
APIGroup string `json:"apiGroup"`
// Kind is the kind of the resource being referenced.
// +kubebuilder:validation:MinLength=1
Kind string `json:"kind"`
// Name is the name of the resource being referenced.
// Defaults to the name of the referencing resource, if not specified.
// +optional
Name string `json:"name,omitempty"`
}

type K8sConfiguration struct {
// Version is the k8s version of the cluster.
Version string `json:"version,omitempty"`
}

// ClusterStatus defines the observed state of Cluster
type ClusterStatus struct {
CommonStatus `json:",inline"`

// Phase is the current phase of the cluster.
Phase ClusterPhase `json:"phase"`

// ProviderStatus is the provider-specific status of the cluster.
// x-kubernetes-preserve-unknown-fields: true
// +optional
ProviderStatus *runtime.RawExtension `json:"providerStatus,omitempty"`
}

type ClusterPhase string

const (
// CLUSTER_PHASE_UNKNOWN represents an unknown status for the cluster.
CLUSTER_PHASE_UNKNOWN ClusterPhase = "Unknown"
// CLUSTER_PHASE_READY represents a cluster that is ready.
CLUSTER_PHASE_READY ClusterPhase = "Ready"
// CLUSTER_PHASE_NOT_READY represents a cluster that is not ready.
CLUSTER_PHASE_NOT_READY ClusterPhase = "Not Ready"
// CLUSTER_PHASE_ERROR represents a cluster that could not be reconciled successfully.
CLUSTER_PHASE_ERROR ClusterPhase = "Error"
// CLUSTER_PHASE_DELETING represents a cluster that is being deleted.
CLUSTER_PHASE_DELETING ClusterPhase = "In Deletion"
// CLUSTER_PHASE_DELETING_ERROR represents a cluster that could not be reconciled successfully while being in deletion.
CLUSTER_PHASE_DELETING_ERROR ClusterPhase = "Error In Deletion"
)

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:metadata:labels="openmcp.cloud/cluster=onboarding"
// +kubebuilder:selectablefield:JSONPath=".spec.clusterProfileRef.name"
// +kubebuilder:printcolumn:JSONPath=".spec.purposes",name="Purposes",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.phase`,name="Phase",type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.annotations["clusters.openmcp.cloud/k8sversion"]`,name="Version",type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.annotations["clusters.openmcp.cloud/profile"]`,name="Profile",type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.labels["environment.clusters.openmcp.cloud"]`,name="Env",type=string,priority=10
// +kubebuilder:printcolumn:JSONPath=`.metadata.labels["provider.clusters.openmcp.cloud"]`,name="Provider",type=string, priority=10
// +kubebuilder:printcolumn:JSONPath=".spec.clusterProfileRef.name",name="ProfileRef",type=string,priority=10
// +kubebuilder:printcolumn:JSONPath=`.metadata.annotations["clusters.openmcp.cloud/providerinfo"]`,name="Info",type=string,priority=10
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// Cluster is the Schema for the clusters API
type Cluster struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec ClusterSpec `json:"spec,omitempty"`
Status ClusterStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// ClusterList contains a list of Cluster
type ClusterList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Cluster `json:"items"`
}

func init() {
SchemeBuilder.Register(&Cluster{}, &ClusterList{})
}

// GetProviderStatus tries to unmarshal the provider status into the given variable.
func (cs *ClusterStatus) GetProviderStatus(into any) error {
return json.Unmarshal(cs.ProviderStatus.Raw, into)
}

// SetProviderStatus marshals the given variable into the provider status.
func (cs *ClusterStatus) SetProviderStatus(from any) error {
b, err := json.Marshal(from)
if err != nil {
return err
}
cs.ProviderStatus = &runtime.RawExtension{Raw: b}
return nil
}

// GetTenancyCount returns the number of ClusterRequests currently pointing to this cluster.
// This is determined by counting the finalizers that have the corresponding prefix.
func (c *Cluster) GetTenancyCount() int {
count := 0
for _, fin := range c.Finalizers {
if strings.HasPrefix(fin, RequestFinalizerOnClusterPrefix) {
count++
}
}
return count
}
58 changes: 58 additions & 0 deletions api/clusters/v1alpha1/clusterprofile_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ClusterProfileSpec defines the desired state of Provider.
type ClusterProfileSpec struct {
// Environment is the environment in which the ClusterProvider resides.
Environment string `json:"environment"`

// ProviderRef is a reference to the ClusterProvider
ProviderRef ObjectReference `json:"providerRef"`

// ProviderConfigRef is a reference to the provider-specific configuration.
ProviderConfigRef ObjectReference `json:"providerConfigRef"`

// SupportedVersions are the supported Kubernetes versions.
SupportedVersions []SupportedK8sVersion `json:"supportedVersions"`
}

type SupportedK8sVersion struct {
// Version is the Kubernetes version.
// +kubebuilder:validation:MinLength=5
Version string `json:"version"`

// Deprecated indicates whether this version is deprecated.
Deprecated bool `json:"deprecated,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster,shortName=cprof;profile
// +kubebuilder:metadata:labels="openmcp.cloud/cluster=onboarding"
// +kubebuilder:selectablefield:JSONPath=".spec.environment"
// +kubebuilder:selectablefield:JSONPath=".spec.providerRef.name"
// +kubebuilder:selectablefield:JSONPath=".spec.providerConfigRef.name"
// +kubebuilder:printcolumn:JSONPath=".spec.environment",name="Env",type=string
// +kubebuilder:printcolumn:JSONPath=".spec.providerRef.name",name="Provider",type=string
// +kubebuilder:printcolumn:JSONPath=".spec.providerConfigRef.name",name="Config",type=string

type ClusterProfile struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec ClusterProfileSpec `json:"spec,omitempty"`
}

// +kubebuilder:object:root=true

type ClusterProfileList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ClusterProfile `json:"items"`
}

func init() {
SchemeBuilder.Register(&ClusterProfile{}, &ClusterProfileList{})
}
Loading
Loading