Repository: GoogleContainerTools/kpt
Pull request #3876 (2023-03-11 18:00:19)

rollouts: fix panic when template type is empty
Fixes a panic that occurs when `rollouts.Spec.SyncTemplate` is nil.


Patch:
diff --git a/rollouts/Makefile b/rollouts/Makefile
index 39e0af92..880cc5b1 100644
--- a/rollouts/Makefile
+++ b/rollouts/Makefile
@@ -55,6 +55,7 @@ help: ## Display this help.
 .PHONY: manifests
 manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
 	$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+	make license
 
 .PHONY: generate
 generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
@@ -68,6 +69,14 @@ fmt: ## Run go fmt against code.
 vet: ## Run go vet against code.
 	go vet ./...
 
+.PHONY: tidy
+tidy: ## Run go mod tidy against code.
+	go mod tidy
+
+.PHONY: license
+license: ## Add licenses.
+	../scripts/update-license.sh
+
 .PHONY: test
 test: manifests generate fmt vet envtest ## Run tests.
 	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out
diff --git a/rollouts/README.md b/rollouts/README.md
index 71160aea..2a4cc40e 100644
--- a/rollouts/README.md
+++ b/rollouts/README.md
@@ -2,4 +2,9 @@
 
 Rollouts is a component for deploying KRM configuration to multiple kubernetes clusters.
 
-More details coming soon.
\ No newline at end of file
+You can run Rollouts:
+
+* On GKE (getting started guide coming soon)
+* [Locally](./docs/running-locally.md), intended primarily for [development](./docs/development.md)
+
+If you already have rollouts installed, there is a user guide coming soon.
diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go
index 0e0f31f9..07b29d4a 100644
--- a/rollouts/controllers/rollout_controller.go
+++ b/rollouts/controllers/rollout_controller.go
@@ -719,6 +719,11 @@ func newRemoteSync(rollout *gitopsv1alpha1.Rollout, target *clusterPackagePair)
 	clusterRef := gitopsv1alpha1.ClusterRef{Name: target.cluster.Name}
 	clusterName := clusterRef.Name[strings.LastIndex(clusterRef.Name, "/")+1:]
 
+	templateType := gitopsv1alpha1.TemplateTypeRootSync
+	if rollout != nil && rollout.Spec.SyncTemplate != nil {
+		templateType = rollout.Spec.SyncTemplate.Type
+	}
+
 	// The RemoteSync object is created in the same namespace as the Rollout
 	// object. The RemoteSync will create either a RepoSync in the same namespace,
 	// or a RootSync in the config-management-system namespace.
@@ -741,7 +746,7 @@ func newRemoteSync(rollout *gitopsv1alpha1.Rollout, target *clusterPackagePair)
 		},
 
 		Spec: gitopsv1alpha1.RemoteSyncSpec{
-			Type:       rollout.Spec.SyncTemplate.Type,
+			Type:       templateType,
 			ClusterRef: clusterRef,
 			Template: &gitopsv1alpha1.Template{
 				Spec:     toSyncSpec(target.packageRef),
diff --git a/rollouts/docs/development.md b/rollouts/docs/development.md
new file mode 100644
index 00000000..82f05817
--- /dev/null
+++ b/rollouts/docs/development.md
@@ -0,0 +1,86 @@
+# Development
+
+## Changing Rollouts API
+
+If you change the API resources, in `api/.../*.go`, update the generated code and manifests by running:
+
+```sh
+make generate
+make manifests
+```
+
+Remove current Rollout and RemoteSync objects from your cluster. Then, remove old CRDs and apply the 
+new CRDs to your cluster by running:
+
+```sh
+make uninstall
+make install
+```
+
+## Components
+
+Rollouts comprises of several software components:
+
+* [api](../api): Definition of the KRM API supported by the rollouts controllers.
+* [config](../config): The generated rollouts manifests.
+* [controllers](../controllers): The controllers and related code for the Rollout CRDs. 
+* [manifests](../manifests): The generated rollouts manifests in kpt package format.
+* [clusterstore](../pkg/clusterstore): Represents a store of kubernetes clusters.
+* [packageclustermatcher](../pkg/packageclustermatcher): Matches discovered packages to known clusters.
+* [packagediscovery](../pkg/packagediscovery): Discovers config packages to be rolled out.
+* [tokenexchange](../pkg/tokenexchange): Exchanges tokens for authenticating GCP service accounts.
+* [rolloutsclient](../rolloutsclient): A client for the rollouts API.
+
+## Running Rollouts
+
+See dedicated documentation on running Rollouts:
+
+* [locally](running-locally.md)
+* on GKE (getting started guide coming soon)
+
+## Build the Container Images
+
+Build Docker images of Rollout components:
+
+```sh
+# Build Images
+make docker-build
+
+# Push Images to Docker Registry
+make docker-push
+
+# Supported make variables:
+# IMG     - image name and tag
+
+# Example:
+gcr.io/<your-project>/rollouts-controller:v0.0.x make docker-push
+```
+
+## Running Locally
+
+Follow [running-locally.md](./running-locally.md) to run Rollouts locally.
+
+## Releasing
+
+Follow [releasing.md](./releasing.md) for the current Rollouts release process.
+
+## Makefile Targets
+
+* `make manifests`: generate Rollouts manifests under `config`
+* `make generate`: generate code based on Rollouts API definitions (runs k8s code generators)
+* `make tidy`: run go mod tidy
+* `make fmt`: formats golang sources
+* `make vet`: vets golang sources
+* `make test`: runs tests
+* `make build`: builds manager binary
+* `make docker-build`: builds Rollouts Docker images
+* `make docker-push`: pushes Rollouts Docker images
+* `make install`: install CRDs into the K8s cluster specified in ~/.kube/config
+* `make uninstall`: uninstall CRDs from the K8s cluster specified in ~/.kube/config
+* `make deploy`: deploy controller to the K8s cluster specified in ~/.kube/config (must provide IMG variable for the controller image)
+* `make undeploy`: undeploy controller from the K8s cluster specified in ~/.kube/config
+* `make license`: add licenses to source code
+
+## VSCode
+
+[VSCode](https://code.visualstudio.com/) works really well for editing and debugging.
diff --git a/rollouts/docs/releasing.md b/rollouts/docs/releasing.md
new file mode 100644
index 00000000..425a065e
--- /dev/null
+++ b/rollouts/docs/releasing.md
@@ -0,0 +1,35 @@
+# Releasing Rollouts
+
+The current release process for Rollouts is manual and subject to change. Here are the steps.
+
+## Build the controller images
+
+First, go to the [kpt-dev](https://console.cloud.google.com/gcr/images/kpt-dev/global/rollouts-controller?project=kpt-dev) to view existing rollouts controller images. Note the previous released version so that
+you can decide what the next release version should be.
+
+For example, if the previous released version is `v0.0.2`, you will most likely want to do `v0.0.3` next.
+
+Set the next release version:
+```sh
+export VERSION=<version>
+```
+
+Build the docker image:
+```sh
+IMG=gcr.io/kpt-dev/rollouts-controller:$VERSION make docker-build
+```
+
+Make sure you are connected to the kpt-dev project, and then push the image:
+```sh
+gcloud config set project kpt-dev
+IMG=gcr.io/kpt-dev/rollouts-controller:$VERSION make docker-push
+```
+
+Run the `create-manifests` script to update the manifests:
+
+```sh
+./scripts/create-manifests.sh --controller-image gcr.io/kpt-dev/rollouts-controller:$VERSION
+```
+
+Create a pull request with the generated changes. Users can now use `kpt pkg get` to pull the
+new Rollout manifests with the updated image.
diff --git a/rollouts/docs/running-locally.md b/rollouts/docs/running-locally.md
new file mode 100644
index 00000000..0a0db6c4
--- /dev/null
+++ b/rollouts/docs/running-locally.md
@@ -0,0 +1,196 @@
+# Running Rollouts Locally
+
+## Prerequisites
+
+To run Rollouts locally, you will need:
+
+* Linux machine (technically it is possible to run Rollouts locally on a Mac but
+  due to differences in Docker between Linux and Mac, the Rollouts scripts are
+  confirmed to work on Linux)
+* [go 1.19](https://go.dev/dl/) or newer
+* `make`
+* Access to GKE clusters.
+
+## Cluster Setup
+
+### Creating a management cluster with KCC running
+First, you must create a config-controller cluster. You can follow the instructions on the 
+[config-controller-setup guide](https://cloud.google.com/anthos-config-management/docs/how-to/config-controller-setup) to create a config-controller cluster. 
+
+Make sure your kubeconfig is connected to this cluster:
+
+```sh
+KUBECONFIG=~/.kube/admin-cluster gcloud container clusters get-credentials <your cluster> --region <your region> --project <your project>
+```
+
+### Provisioning child clusters
+The next step will be to provision new child clusters. This example names the child cluster `gke-n`;
+you can replace `gke-n` with whatever name you want for your child cluster. 
+
+```sh
+# first step is to create a deployable instance of the package
+kpt pkg get git@github.com:droot/kpt-packages.git/cluster@main gke-n --for-deployment
+
+# now render the package
+kpt fn render
+
+# assuming your kubectl is configured to talk to a management cluster with KCC running (the previous step).
+# provision the cluster using following commands
+KUBECONFIG=~/.kube/admin-cluster kpt live init gke-n
+KUBECONFIG=~/.kube/admin-cluster kpt live apply gke-n
+```
+
+You can repeat the above steps to create as many child clusters as you want.
+
+### Setting up kubeconfig
+Next, we will configure kubeconfig to be able to talk to each cluster individually.
+
+```sh
+# once kpt live status is showing current for a cluster package
+# run the following
+KUBECONFIG=~/.kube/gke-n gcloud container clusters get-credentials gke-n --region us-west1 --project <your project>
+
+# now ~/.kube/gke-n file has been updated with the credentials for gke-n cluster
+# verify if this is working
+KUBECONFIG=~/.kube/gke-n kubectl get pods -n kube-system
+```
+
+### Set up Config Sync
+Rollouts requires a git syncer installed on child clusters. Currently, only Config Sync is supported. 
+To install Config Sync on your child clusters, follow these steps.
+
+First, set the release version of Config Sync that you would like to install:
+
+```sh
+export CS_VERSION=vX.Y.Z
+```
+
+Then, apply the core Config Sync manifests to your cluster:
+
+```sh
+KUBECONFIG=~/.kube/gke-n kubectl apply -f "https://github.com/GoogleContainerTools/kpt-config-sync/releases/download/${CS_VERSION}/config-sync-manifest.yaml"
+```
+
+Then, for kubernetes version v1.25 and newer, optionally apply the asm.yaml manifest:
+
+```sh
+KUBECONFIG=~/.kube/gke-n kubectl apply -f "https://github.com/GoogleContainerTools/kpt-config-sync/releases/download/${CS_VERSION}/acm-psp.yaml"
+```
+
+If you wish to install Config Sync from source instead of using a released version, you can follow
+the [Config Sync installation guide](https://github.com/GoogleContainerTools/kpt-config-sync/blob/main/docs/installation.md).
+
+
+## Running Rollouts locally
+
+Clone this repository into `${GOPATH}/src/github.com/GoogleContainerTools/kpt`.
+
+```sh
+git clone https://github.com/GoogleContainerTools/kpt.git "${GOPATH}/src/github.com/GoogleContainerTools/kpt"
+```
+
+Enter the rollouts directory:
+
+```
+cd rollouts
+```
+
+Download dependencies:
+
+```sh
+make tidy
+```
+
+Assuming your kubeconfig is configured to talk to the management cluster with KCC installed (that you created
+in the above steps), apply the manifests:
+
+```sh
+KUBECONFIG=~/.kube/admin-cluster make install
+```
+
+Confirm the CRDs are installed:
+
+```sh
+KUBECONFIG=~/.kube/admin-cluster kubectl api-resources | grep gitops.kpt.dev
+
+progressiverolloutstrategies  gitops.kpt.dev/v1alpha1  true  ProgressiveRolloutStrategy
+remotesyncs                   gitops.kpt.dev/v1alpha1  true  RemoteSync
+rollouts                      gitops.kpt.dev/v1alpha1  true  Rollout
+```
+
+Now you are ready to run the controller locally:
+
+```sh
+KUBECONFIG=~/.kube/admin-cluster go run main.go
+```
+
+## Creating a Rollout object
+
+Here is an example of a simple `Rollout` object:
+
+```yaml
+apiVersion: gitops.kpt.dev/v1alpha1
+kind: Rollout
+metadata:
+  name: sample
+spec:
+  description: sample
+  clusters:
+    sourceType: KCC
+  packages:
+    sourceType: GitHub
+    github:
+      selector:
+        org: droot
+        repo: store
+        directory: namespaces
+        revision: main
+  targets:
+    selector:
+      matchLabels:
+        location/city: example
+  syncTemplate:
+    type: RootSync
+  packageToTargetMatcher:
+    type: AllClusters
+  strategy:
+    type: RollingUpdate
+    rollingUpdate:
+      maxConcurrent: 2
+```
+
+Apply this to your management cluster with `kubectl apply -f`. View the created Rollout, RemoteSyncs, and RootSync objects to verify that the controller is running properly:
+
+```sh
+# see the rollouts object
+KUBECONFIG=~/.kube/admin-cluster kubectl get rollouts sample
+
+# see the remotesync objects that the rollouts controller created
+KUBECONFIG=~/.kube/admin-cluster kubectl get remotesyncs
+
+# see the rootsync object that the remotesync controller created
+KUBECONFIG=~/.kube/gke-n kubectl get rootsyncs -nconfig-management-system
+```
+
+Deleting the Rollout object should likewise delete the associated RemoteSync and Rootsync objects. You can 
+look at the controller logs to verify that the various Remotesync/Rootsync objects are being created, updated,
+or deleted.
+
+## Restarting Rollouts
+
+If you make code changes, all you have to do is stop the controller (ctrl+C in the terminal where it is running),
+and rerun `go run main.go`.
+
+If you make changes to the Rollouts API, refer to the `Changing Rollouts API` section in the [development guide](./development.md).
+
+## Troubleshooting
+
+### Deleting the Rollout object
+
+The controllers must be running when you delete the Rollout object; otherwise the finalizer will prevent deletion. If you find yourself stuck on deleting a Rollout or RemoteSync object due to an API change or change
+in the controller code, you can manually remove the finalizer from the object using `kubectl edit`. 
+
+### API Changes
+
+Make sure you reinstall the CRDs if there are changes to the API. Failure to do so can result in unexpected
+behavior in the controllers.
diff --git a/rollouts/scripts/create-manifests.sh b/rollouts/scripts/create-manifests.sh
index 15a1b8f9..a3bf4579 100755
--- a/rollouts/scripts/create-manifests.sh
+++ b/rollouts/scripts/create-manifests.sh
@@ -97,4 +97,8 @@ function set_controller_image {
 validate
 generate_crds
 generate_controller_manifests
-set_controller_image ${CONTROLLER_IMAGE}
\ No newline at end of file
+set_controller_image ${CONTROLLER_IMAGE}
+
+# cleanup
+make license
+kpt fn render manifests
\ No newline at end of file


Test patch:
diff --git a/rollouts/controllers/remoterootsync_controller_test.go b/rollouts/controllers/remoterootsync_controller_test.go
index b58db7a4..7e254c17 100644
--- a/rollouts/controllers/remoterootsync_controller_test.go
+++ b/rollouts/controllers/remoterootsync_controller_test.go
@@ -1,3 +1,17 @@
+// Copyright 2023 Google LLC
+//
+// 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 (