Skip to content

Commit d972166

Browse files
authored
Support for CLI and JSON-styled startup configs (#37)
* typo fix * remove unused kustomization file * added cimments about startup config predefined name * renamed main reconciliation function * initial skeleton for startupcfg handler and status * rename found->pod * added readiness probe and skeleton for scrpaligo provisioning * refactor startup config handling and controller status updates * added docs on how to stream controller logs * address linter * address more linter checks * remove unused received and used var declaration on empty slice * do startup config check at a caller side * added e2e test for startup config * address lint * added matrix test for e2e * increase timeout * rename func * rename * defer driver closing in parent func * added checkpoint provisioning * added commit save to persist the config * checkout before setting go * crank up ready timer even more * added ready check for bare CR * split e2e test functions since gh actions borked * added readme and bump version
1 parent c42d638 commit d972166

File tree

17 files changed

+3095
-138
lines changed

17 files changed

+3095
-138
lines changed

.github/workflows/e2e.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,23 @@ jobs:
2121
e2e:
2222
name: End-to-end test
2323
runs-on: ubuntu-latest
24+
strategy:
25+
fail-fast: false
26+
matrix:
27+
test:
28+
[
29+
"TestSrlinuxReconciler_BareSrlinuxCR",
30+
"TestSrlinuxReconciler_WithJSONStartupConfig",
31+
"TestSrlinuxReconciler_WithCLIStartupConfig",
32+
]
2433

2534
steps:
2635
- name: Set env vars
2736
run: |
2837
echo "KNE_REF=${{ inputs.kne_ref }}" >> $GITHUB_ENV
2938
39+
- uses: actions/checkout@v3
40+
3041
- name: Set up Go
3142
uses: actions/setup-go@v4
3243
with:
@@ -39,12 +50,10 @@ jobs:
3950
version: ${{ inputs.kind_version }}
4051
skipClusterCreation: true
4152

42-
- uses: actions/checkout@v3
43-
4453
- name: Prepare e2e environment
4554
run: make prepare-e2e-env
4655

4756
- name: Run e2e tests
4857
# this test ensures that srl-controller (built from referenced source) can be succesfully installed on a KNE cluster
4958
# for a using specified versions of KNE/KinD
50-
run: make test-e2e
59+
run: E2E_TEST_NAME=${{ matrix.test }} make test-e2e

.mk/e2e.mk

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,21 @@ install-srl-controller: ## Install srl-controller from current working dir
3636
@echo "wait for controller manager to be ready"
3737
kubectl -n srlinux-controller wait --for=condition=Available deployment.apps/srlinux-controller-controller-manager
3838

39+
.PHONY: uninstall-srl-controller
40+
uninstall-srl-controller: ## Uninstall srl-controller from current working dir
41+
kubectl delete -k config/default
42+
3943
.PHONY: kind-load-image
4044
kind-load-image: ## Load SR Linux container image to kind cluster
4145
docker pull ${SRL_IMAGE}
42-
kind load docker-image ${SRL_IMAGE} --name srl-test
46+
kind load docker-image ${SRL_IMAGE} --name ${KIND_CLUSTER_NAME}
47+
48+
.PHONY: start-kne-cluster
49+
start-kne-cluster: install-kne kne-test-deployment-cfg-file deploy-kne kind-load-image ## Deploy KNE kind cluster but do not install any controllers
4350

4451
.PHONY: prepare-e2e-env
4552
prepare-e2e-env: install-kne kne-test-deployment-cfg-file deploy-kne temp-docker-build install-srl-controller kind-load-image ## Install srl-controller from current working dir
4653

4754
.PHONY: test-e2e
48-
test-e2e: ## Test e2e using kind
49-
go test -v github.com/srl-labs/srl-controller/tests/e2e
55+
test-e2e: ## Test e2e using kind and a provided test name
56+
go test -timeout 5m -v github.com/srl-labs/srl-controller/tests/e2e -run ${E2E_TEST_NAME}

README.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ Once the kne+kind cluster is created and the `srl-controller` is installed onto
6262
kne create examples/srlinux/2node-srl-with-config.pbtxt
6363
```
6464

65+
Note, that controller logs can be viewed live with:
66+
67+
```bash
68+
kubectl logs --follow -n srlinux-controller $(kubectl get pods -A | grep srlinux-controller | awk '{print $2}')
69+
```
70+
6571
This will deploy the SR Linux nodes and will create k8s services as per the topology configuration. The services will be exposed via MetalLB and can be queried as:
6672

6773
```text
@@ -106,30 +112,27 @@ When a request to create a `Srlinux` resource named `r1` in namespace `ns` comes
106112

107113
1. Checks if the pods exist within a namespace `ns` with a name `r1`
108114
2. If the pod hasn't been found, then the controller first ensures that the necessary config maps exist in namespace `ns` and creates them otherwise.
109-
3. When config maps are sorted out, the controller schedules a pod with the name `r1` and requeue the request
110-
4. In a requeue run, the pod is now found and the controller updates the status of `Srlinux` resource with the image name that was used in the pod spec.
115+
3. When config maps are sorted out, the controller schedules a pod with the name `r1` and requeues the request.
116+
4. If a startup-config was provided, the controller loads this config using SSH into the pod, creates a named checkpoint "initial" and requeues the request.
117+
5. In a requeue run, the pod is now found and the controller updates the status of `Srlinux` resource.
111118

112119
### Deletion
113120

114121
When a deletion happens on `Srlinux` resource, the reconcile loop does nothing.
115122

116-
### API access
117-
118-
This repo contains a clientset for API access to the `Srlinux` custom resource. Check [kne repo](https://github.com/openconfig/kne/blob/fc195a73035bcbf344791979ca3e067be47a249c/topo/node/srl/srl.go#L46) to see how this can be done.
119-
120123
## Building `srl-controller` container image
121124

122125
To build `srl-controller` container image, execute:
123126

124127
```bash
125128
# don't forget to set the correct tag
126-
# for example make docker-build IMG=ghcr.io/srl-labs/srl-controller:0.4.3
129+
# for example make docker-build IMG=ghcr.io/srl-labs/srl-controller:v0.6.0
127130
make docker-build IMG=ghcr.io/srl-labs/srl-controller:${tag}
128131
```
129132

130133
> build process will try to remove license headers for some manifests, discard those changes.
131134
132-
Next update the controller version in [manager/kustomization.yaml](config/manager/kustomization.yaml) kustomization file to match the newly built version.
135+
Next, update the controller version in [manager/kustomization.yaml](config/manager/kustomization.yaml) kustomization file to match the newly built version.
133136

134137
Finally, upload the container image to the registry:
135138

@@ -141,6 +144,12 @@ docker tag ghcr.io/srl-labs/srl-controller:${tag} ghcr.io/srl-labs/srl-controlle
141144
docker push ghcr.io/srl-labs/srl-controller:latest
142145
```
143146

147+
Note, update the SR Linux manifest in the [KNE repo](https://github.com/openconfig/kne/) to use the new version of the controller. To generate the manifest, run:
148+
149+
```bash
150+
kustomize build config/default
151+
```
152+
144153
## Developers guide
145154

146155
Developers should deploy the controller onto a cluster from the source code. Ensure that the `srl-controller` is [uninstalled](#uninstall) from the cluster before proceeding.

api/v1/srlinux_types.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,31 @@ type SrlinuxSpec struct {
5656

5757
// SrlinuxStatus defines the observed state of Srlinux.
5858
type SrlinuxStatus struct {
59+
// Status is the status of the srlinux custom resource.
60+
// Can be one of: "created", "running", "error".
61+
Status string `json:"status,omitempty"`
5962
// Image used to run srlinux pod
6063
Image string `json:"image,omitempty"`
64+
// StartupConfig contains the status of the startup-config.
65+
StartupConfig StartupConfigStatus `json:"startup-config,omitempty"`
66+
// Ready is true if the srlinux NOS is ready to receive config.
67+
// This is when management server is running and initial commit is processed.
68+
Ready bool `json:"ready,omitempty"`
69+
}
70+
71+
type StartupConfigStatus struct {
72+
// Phase is the phase startup-config is in. Can be one of: "pending", "loaded", "not-provided", "failed".
73+
Phase string `json:"phase,omitempty"`
6174
}
6275

6376
//+kubebuilder:object:root=true
6477
//+kubebuilder:subresource:status
6578

6679
// Srlinux is the Schema for the srlinuxes API.
67-
// +kubebuilder:printcolumn:name="Image",type="string",JSONPath=".status.image"
6880
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
81+
// +kubebuilder:printcolumn:name="Image",type="string",JSONPath=".status.image"
82+
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
83+
// +kubebuilder:printcolumn:name="Ready",type="boolean",JSONPath=".status.ready"
6984
type Srlinux struct {
7085
metav1.TypeMeta `json:",inline"`
7186
metav1.ObjectMeta `json:"metadata,omitempty"`

api/v1/zz_generated.deepcopy.go

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/kne.srlinux.dev_srlinuxes.yaml

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@ spec:
1616
scope: Namespaced
1717
versions:
1818
- additionalPrinterColumns:
19-
- jsonPath: .status.image
20-
name: Image
21-
type: string
2219
- jsonPath: .metadata.creationTimestamp
2320
name: Age
2421
type: date
22+
- jsonPath: .status.image
23+
name: Image
24+
type: string
25+
- jsonPath: .status.status
26+
name: Status
27+
type: string
28+
- jsonPath: .status.ready
29+
name: Ready
30+
type: boolean
2531
name: v1
2632
schema:
2733
openAPIV3Schema:
@@ -122,6 +128,23 @@ spec:
122128
image:
123129
description: Image used to run srlinux pod
124130
type: string
131+
ready:
132+
description: Ready is true if the srlinux NOS is ready to receive
133+
config. This is when management server is running and initial commit
134+
is processed.
135+
type: boolean
136+
startup-config:
137+
description: StartupConfig contains the status of the startup-config.
138+
properties:
139+
phase:
140+
description: 'Phase is the phase startup-config is in. Can be
141+
one of: "pending", "loaded", "not-provided", "failed".'
142+
type: string
143+
type: object
144+
status:
145+
description: 'Status is the status of the srlinux custom resource.
146+
Can be one of: "created", "running", "error".'
147+
type: string
125148
type: object
126149
type: object
127150
served: true

config/manager/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ kind: Kustomization
1010
images:
1111
- name: controller
1212
newName: ghcr.io/srl-labs/srl-controller
13-
newTag: v0.5.0
13+
newTag: v0.6.0

controllers/manifests/variants/kustomization.yml

Lines changed: 0 additions & 7 deletions
This file was deleted.

controllers/pod.go

Lines changed: 20 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"context"
99
"fmt"
1010

11-
"github.com/go-logr/logr"
1211
knenode "github.com/openconfig/kne/topo/node"
1312
srlinuxv1 "github.com/srl-labs/srl-controller/api/v1"
1413
corev1 "k8s.io/api/core/v1"
@@ -24,6 +23,10 @@ const (
2423
licenseFileName = "license.key"
2524
licenseMntPath = "/opt/srlinux/etc/license.key"
2625
licenseMntSubPath = "license.key"
26+
readinessFile = "/etc/opt/srlinux/devices/app_ephemeral.mgmt_server.ready_for_config"
27+
readinessInitialDelay = 10
28+
readinessPeriodSeconds = 5
29+
readinessFailureThreshold = 10
2730
)
2831

2932
// podForSrlinux returns a srlinux Pod object.
@@ -52,7 +55,9 @@ func (r *SrlinuxReconciler) podForSrlinux(
5255
}
5356

5457
// handle startup config volume mounts if the startup config was defined
55-
handleStartupConfig(s, pod, log)
58+
if s.Spec.Config.ConfigDataPresent {
59+
createStartupConfigVolumesAndMounts(s, pod, log)
60+
}
5661

5762
//nolint:godox
5863
// TODO: handle the error
@@ -98,6 +103,19 @@ func createContainers(s *srlinuxv1.Srlinux) []corev1.Container {
98103
RunAsUser: pointer.Int64(0),
99104
},
100105
VolumeMounts: createVolumeMounts(s),
106+
ReadinessProbe: &corev1.Probe{
107+
ProbeHandler: corev1.ProbeHandler{
108+
Exec: &corev1.ExecAction{
109+
Command: []string{
110+
"cat",
111+
readinessFile,
112+
},
113+
},
114+
},
115+
InitialDelaySeconds: readinessInitialDelay,
116+
PeriodSeconds: readinessPeriodSeconds,
117+
FailureThreshold: readinessFailureThreshold,
118+
},
101119
}}
102120
}
103121

@@ -171,50 +189,6 @@ func createVolumes(s *srlinuxv1.Srlinux) []corev1.Volume {
171189
return vols
172190
}
173191

174-
// handleStartupConfig creates volume mounts and volumes for srlinux pod
175-
// if the (startup) config file was provided in the spec.
176-
// Volume mounts happens in the /tmp/startup-config directory and not in the /etc/opt/srlinux
177-
// because we need to support renaming operations on config.json, and bind mount paths are not allowing this.
178-
// Hence the temp location, from which the config file is then copied to /etc/opt/srlinux by the kne-entrypoint.sh.
179-
func handleStartupConfig(s *srlinuxv1.Srlinux, pod *corev1.Pod, log logr.Logger) {
180-
// initialize config path and config file variables
181-
cfgPath := defaultConfigPath
182-
if p := s.Spec.GetConfig().ConfigPath; p != "" {
183-
cfgPath = p
184-
}
185-
186-
// only create startup config mounts if the config data was set in kne
187-
if s.Spec.Config.ConfigDataPresent {
188-
log.Info(
189-
"Adding volume for startup config to pod spec",
190-
"volume.name",
191-
"startup-config-volume",
192-
"mount.path",
193-
cfgPath,
194-
)
195-
196-
pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
197-
Name: "startup-config-volume",
198-
VolumeSource: corev1.VolumeSource{
199-
ConfigMap: &corev1.ConfigMapVolumeSource{
200-
LocalObjectReference: corev1.LocalObjectReference{
201-
Name: fmt.Sprintf("%s-config", s.Name),
202-
},
203-
},
204-
},
205-
})
206-
207-
pod.Spec.Containers[0].VolumeMounts = append(
208-
pod.Spec.Containers[0].VolumeMounts,
209-
corev1.VolumeMount{
210-
Name: "startup-config-volume",
211-
MountPath: cfgPath,
212-
ReadOnly: false,
213-
},
214-
)
215-
}
216-
}
217-
218192
func createVolumeMounts(s *srlinuxv1.Srlinux) []corev1.VolumeMount {
219193
vms := []corev1.VolumeMount{
220194
{

0 commit comments

Comments
 (0)