-
Notifications
You must be signed in to change notification settings - Fork 69
WIP: Adding initial proof of concept creating nested CRDs for each object #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
|
||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
bin | ||
|
||
# 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 | ||
*.swp | ||
*.swo | ||
*~ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Build the manager binary | ||
FROM golang:1.13 as builder | ||
|
||
WORKDIR /workspace | ||
# Copy the Go Modules manifests | ||
COPY go.mod go.mod | ||
COPY go.sum go.sum | ||
# cache deps before building and copying source so that we don't need to re-download as much | ||
# and so that source changes don't invalidate our downloaded layer | ||
RUN go mod download | ||
|
||
# Copy the go source | ||
COPY main.go main.go | ||
COPY apis/ apis/ | ||
COPY cmd/ cmd/ | ||
COPY constants/ constants/ | ||
COPY controllers/ controllers/ | ||
COPY kubeconfig/ kubeconfig/ | ||
COPY pki/ pki/ | ||
COPY secret/ secret/ | ||
COPY utils/ utils/ | ||
COPY webhooks/ webhooks/ | ||
|
||
|
||
# Build | ||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager 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 nonroot:nonroot | ||
|
||
ENTRYPOINT ["/manager"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
|
||
# Image URL to use all building/pushing image targets | ||
IMG ?= controller:latest | ||
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion) | ||
CRD_OPTIONS ?= "crd:trivialVersions=true,crdVersions=v1" | ||
# Selects specific packages to test | ||
PACKAGES ?= "./..." | ||
|
||
# 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 | ||
|
||
all: manager | ||
|
||
# Run tests | ||
test: generate fmt vet manifests | ||
go test ${PACKAGES} -coverprofile cover.out | ||
|
||
# Build manager binary | ||
manager: generate fmt vet | ||
go build -o bin/manager ./cmd/manager/ | ||
|
||
# Run against the configured Kubernetes cluster in ~/.kube/config | ||
run: generate fmt vet manifests | ||
go run ./cmd/manager/ | ||
|
||
# Install CRDs into a cluster | ||
install: manifests | ||
kustomize build config/crd | kubectl apply -f - | ||
|
||
# Uninstall CRDs from a cluster | ||
uninstall: manifests | ||
kustomize build config/crd | kubectl delete -f - | ||
|
||
# Deploy controller in the configured Kubernetes cluster in ~/.kube/config | ||
deploy: manifests | ||
cd config/manager && kustomize edit set image controller=${IMG} | ||
kustomize build config/default | kubectl apply -f - | ||
|
||
# Generate manifests e.g. CRD, RBAC etc. | ||
manifests: controller-gen | ||
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases | ||
|
||
# Run go fmt against code | ||
fmt: | ||
go fmt ./... | ||
|
||
# Run go vet against code | ||
vet: | ||
go vet ./... | ||
|
||
# Generate code | ||
generate: controller-gen | ||
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." | ||
|
||
# Build the docker image | ||
docker-build: test | ||
docker build . -t ${IMG} | ||
|
||
# Push the docker image | ||
docker-push: | ||
docker push ${IMG} | ||
|
||
# find or download controller-gen | ||
# download controller-gen if necessary | ||
controller-gen: | ||
ifeq (, $(shell which controller-gen)) | ||
@{ \ | ||
set -e ;\ | ||
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ | ||
cd $$CONTROLLER_GEN_TMP_DIR ;\ | ||
go mod init tmp ;\ | ||
go get sigs.k8s.io/controller-tools/cmd/[email protected] ;\ | ||
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ | ||
} | ||
CONTROLLER_GEN=$(GOBIN)/controller-gen | ||
else | ||
CONTROLLER_GEN=$(shell which controller-gen) | ||
endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
domain: cluster.x-k8s.io | ||
multigroup: true | ||
repo: sigs.k8s.io/cluster-api-provider-nested | ||
resources: | ||
- group: controlplane | ||
kind: NestedControlPlane | ||
version: v1alpha3 | ||
- group: controlplane | ||
kind: NestedControlPlaneTemplate | ||
version: v1alpha3 | ||
- group: controlplane | ||
kind: NestedEtcd | ||
version: v1alpha3 | ||
- group: controlplane | ||
kind: NestedPKI | ||
version: v1alpha3 | ||
version: "2" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
Copyright 2020 The Kubernetes Authors. | ||
|
||
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 apis | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/runtime" | ||
|
||
controlplanev1alpha3 "sigs.k8s.io/cluster-api-provider-nested/apis/controlplane/v1alpha3" | ||
// +kubebuilder:scaffold:imports | ||
) | ||
|
||
// AddToScheme will register all schemes needed to run CAPN | ||
func AddToScheme(scheme *runtime.Scheme) (err error) { | ||
if err = controlplanev1alpha3.AddToScheme(scheme); err != nil { | ||
return err | ||
} | ||
// +kubebuilder:scaffold:scheme | ||
return | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
Copyright 2020 The Kubernetes Authors. | ||
|
||
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 v1alpha3 contains API Schema definitions for the controlplane v1alpha3 API group | ||
// +kubebuilder:object:generate=true | ||
// +groupName=controlplane.cluster.x-k8s.io | ||
package v1alpha3 | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
"sigs.k8s.io/controller-runtime/pkg/scheme" | ||
) | ||
|
||
var ( | ||
// GroupVersion is group version used to register these objects | ||
GroupVersion = schema.GroupVersion{Group: "controlplane.cluster.x-k8s.io", Version: "v1alpha3"} | ||
|
||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme | ||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} | ||
|
||
// AddToScheme adds the types in this group-version to the given scheme. | ||
AddToScheme = SchemeBuilder.AddToScheme | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* | ||
Copyright 2020 The Kubernetes Authors. | ||
|
||
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 v1alpha3 | ||
|
||
import ( | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
// NestedControlPlaneSpec defines the desired state of NestedControlPlane | ||
type NestedControlPlaneSpec struct { | ||
// ClusterNamespace allows you to specify the namespace where the control | ||
// plane components are run, if you don't specify one then the namespace | ||
// will be in the format of {namespace}-{vcp.uid}. This also makes up the | ||
// prefixes used for all Namespaces in the cluster. | ||
// e.g. {namespace}-{vcp.uid}-{vc-namespace} | ||
// +optional | ||
ClusterNamespace string `json:"clusterNamespace,omitempty"` | ||
|
||
// ClusterDomain allows you to specify the domain name of the nested | ||
// cluster. e.g. a pod dns will be | ||
// {some-pod}.{some-namespace}.svc.{ClusterDomain} | ||
// +kubebuilder:default=cluster.local | ||
ClusterDomain string `json:"clusterDomain,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should also have |
||
|
||
// TemplateRef allows you to specify the control plane template to create | ||
// the control plane. | ||
// +optional | ||
TemplateRef *corev1.ObjectReference `json:"configRef,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just to clarify on what a template is here: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This TemplateRef will refer to an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a reference to an object that itself describes how to provision the cluster? Does this refer to a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should create a default |
||
|
||
// PKIExpireDays allows you to specify the period of time the nested | ||
// cluster PKI, if not set the PKI will expire after 1 year. | ||
// +optional | ||
// +kubebuilder:default=365 | ||
PKIExpireDays int64 `json:"pkiExpireDays,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw PKI and had some quick thoughts:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should err on the side of caution with adding fields. If we don't need to expose this as a user-configurable option yet, I think we should not do it. The above questions raise great points, and I am not sure we have enough of an understanding of the problem space to know where best to put it just yet.
👍 - I also think this is a question we should answer more generally. What is the purpose of the NestedControlPlane vs the NestedControlPlaneTemplate resource? What defines what should go where? |
||
|
||
// TransparentMetaPrefixes allows you to specify the key prefix of labels | ||
// or annotations that should be back populated to Nested Cluster. These | ||
// meta data are generated by super master controllers, which are needed by | ||
// nested cluster to interact with external systems. | ||
// +optional | ||
TransparentMetaPrefixes []string `json:"transparentMetaPrefixes,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not too sure what this means - does "back populated" mean synced? It'd really help if we had a proposal document that explained some of this stuff and set out some definitions. |
||
|
||
// OpaqueMetaPrefixes allows you to specify the key prefix of labels or | ||
// annotations that are not visible to Nested Cluster but are kept in | ||
// super master. For example, the annotations added by syncer controller. | ||
// +optional | ||
OpaqueMetaPrefixes []string `json:"opaqueMetaPrefixes,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be removed an implemented somewhere else since this specific to VC |
||
} | ||
|
||
// NestedControlPlaneStatus defines the observed state of NestedControlPlane | ||
type NestedControlPlaneStatus struct { | ||
// ClusterNamespace denotes the namespace where the control plane components | ||
// are deployed into. | ||
// +optional | ||
ClusterNamespace string `json:"clusterNamespace,omitempty"` | ||
|
||
// Ready denotes that the NestedControlPlane API Server is ready to | ||
// receive requests and that the VPC infra is ready. | ||
// +optional | ||
Ready bool `json:"ready"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this field should exist as-is. If we are going to have an overarching concept of readiness, it should be denoted as a condition. That said, I think we should be careful to clearly define what we call ready or not. If an apiserver is failing 50% of requests, is it ready? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can probably use |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should probably also include a field to signal error here. E.g
Though this can probably be detailed later when introducing conditions |
||
// Conditions specifies the cpnditions for the managed control plane | ||
// Conditions clusterv1.Conditions `json:"conditions,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we going to use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, we need to have conditions on these types. This should follow the base required fields from CAPI - https://cluster-api.sigs.k8s.io/developer/architecture/controllers/control-plane.html |
||
} | ||
|
||
// +kubebuilder:storageversion | ||
// +kubebuilder:subresource:status | ||
// +kubebuilder:object:root=true | ||
// +kubebuilder:resource:scope=Namespaced,categories=cluster-api;capn;capi,shortName=ncp | ||
// +kubebuilder:printcolumn:name="ClusterNamespace",type="string",JSONPath=".status.clusterNamespace",description="Namespace of the cluster",priority=1 | ||
|
||
// NestedControlPlane is the Schema for the nestedcontrolplanes API | ||
type NestedControlPlane struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just out of curiosity if there has been any discussion for naming nested vs hosted? |
||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
||
Spec NestedControlPlaneSpec `json:"spec,omitempty"` | ||
Status NestedControlPlaneStatus `json:"status,omitempty"` | ||
} | ||
|
||
// +kubebuilder:object:root=true | ||
|
||
// NestedControlPlaneList contains a list of NestedControlPlane | ||
type NestedControlPlaneList struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ListMeta `json:"metadata,omitempty"` | ||
Items []NestedControlPlane `json:"items"` | ||
} | ||
|
||
func init() { | ||
SchemeBuilder.Register(&NestedControlPlane{}, &NestedControlPlaneList{}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
Copyright 2020 The Kubernetes Authors. | ||
|
||
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 v1alpha3 | ||
|
||
import ( | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"strings" | ||
|
||
ctrl "sigs.k8s.io/controller-runtime" | ||
logf "sigs.k8s.io/controller-runtime/pkg/log" | ||
"sigs.k8s.io/controller-runtime/pkg/webhook" | ||
) | ||
|
||
// log is for logging in this package. | ||
var nestedcontrolplanelog = logf.Log.WithName("nestedcontrolplane-resource") | ||
|
||
func (r *NestedControlPlane) SetupWebhookWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewWebhookManagedBy(mgr). | ||
For(r). | ||
Complete() | ||
} | ||
|
||
// TODO(christopherhein): we need to add validations here on whether the root | ||
// namespace isn't taken | ||
|
||
// +kubebuilder:webhook:path=/mutate/controlplane/v1alpha3/nestedcontrolplane,mutating=true,failurePolicy=fail,groups=controlplane.cluster.x-k8s.io,resources=nestedcontrolplanes,verbs=create;update,versions=v1alpha3,name=mnestedcontrolplane.kb.io | ||
|
||
var _ webhook.Defaulter = &NestedControlPlane{} | ||
|
||
// Default implements webhook.Defaulter so a webhook will be registered for the type | ||
func (r *NestedControlPlane) Default() { | ||
nestedcontrolplanelog.Info("default", "name", r.Name) | ||
|
||
// if clusterNamespace not set then set using {namespace}-{vcp.uid}-{vc-namespace} | ||
if r.Spec.ClusterNamespace == "" { | ||
digest := sha256.Sum256([]byte(r.GetUID())) | ||
namespaceSlice := []string{r.Namespace, hex.EncodeToString(digest[0:])[0:6], r.Name} | ||
namespace := strings.Join(namespaceSlice, "-") | ||
r.Spec.ClusterNamespace = namespace | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the
vcp.uid
? We should make these doc comments as clear as possible for the 'layman' user and avoid ambiguous terms if possible 😄This seems okay for now. In future though it may be worth having some kind of
namespacePrefix
field to explicitly set this to some other value. It may be worth noting here that whilst it is true today, it should not be relied upon? If so, can we expose the chosen prefix through a status field to make it easier for other consumers of this API to make use of/build upon?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I think there needs to be a length restriction on this field. We should also call out how the length of this field influences the restrictions on namespace names within nested clusters too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for the
namespacePrefix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to call out this is typically defaulted and that it needs to have a max length validation and warning about it's usage for conflicting namespace prefixes, also rename to
NamespacePrefix