Skip to content

Commit

Permalink
Merge pull request #12 from srl-labs/srl-license
Browse files Browse the repository at this point in the history
srlinux license provisioning via secrets
  • Loading branch information
hellt authored Jul 31, 2022
2 parents 02f0cb4 + 8759a6b commit ec1c446
Show file tree
Hide file tree
Showing 23 changed files with 823 additions and 120 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/linters/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,20 @@ linters:
- goerr113
- nestif
- prealloc
- testpackage
- wsl
linters-settings:
lll:
line-length: 140

issues:
# https://github.com/golangci/golangci-lint/issues/2439#issuecomment-1002912465
exclude-use-default: false
# exclude-use-default: false
exclude-rules:
- path: _test\.go
linters:
- gomnd
- dupl
- structcheck
- unused
- unparam
# - gomnd
# - dupl
# - structcheck
# - unused
# - unparam
- funlen
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# Binaries for programs and plugins
*.exe
*.exe~
Expand All @@ -24,3 +23,5 @@ testbin/*
*.swo
*~
.vscode

private
5 changes: 4 additions & 1 deletion .mk/lint.mk
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ GODOT_FLAGS := -w .
GOLINES_CMD := docker run --rm -it -v $(shell pwd):/work ghcr.io/hellt/golines:0.10.0 golines
GOLINES_FLAGS := -w .

GOLANGCI_CMD := docker run -it --rm -v $$(pwd):/app -w /app golangci/golangci-lint:v1.47.2 golangci-lint
GOLANGCI_CMD := docker run -it --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v1.47.2 golangci-lint
GOLANGCI_FLAGS := --config ./.github/workflows/linters/.golangci.yml run -v --fix


# when running in a CI env we use locally installed bind
ifdef CI
GOFUMPT_CMD := gofumpt
GODOT_CMD := godot
GOLINES_CMD := golines
GOLANGCI_CMD := golangci-lint
endif


Expand Down
82 changes: 43 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# SR Linux Controller for KNE

This is a k8s controller for running and managing SR Linux nodes launched from [openconfig/kne](https://github.com/openconfig/kne) topology.

## Install

To install the latest version of this controller on a cluster referenced in `~/.kube/config` issue the following command:

```bash
# latest version
kubectl apply -k https://github.com/srl-labs/srl-controller/config/default
Expand All @@ -11,7 +15,8 @@ kubectl apply -k https://github.com/srl-labs/srl-controller/config/default?ref=v
```

The resources of this controller will be scoped under `srlinux-controller` namespace.
```

```text
❯ kubectl get all -n srlinux-controller
NAME READY STATUS RESTARTS AGE
Expand All @@ -28,91 +33,90 @@ replicaset.apps/srlinux-controller-controller-manager-c7495dcc7 1 1
```

### Installing from a repo

The controller can be installed with make directly from the repo:
```

```text
make deploy IMG=ghcr.io/srl-labs/srl-controller:0.3.1
```

Make sure to check which controller versions are [available](https://github.com/srl-labs/srl-controller/pkgs/container/srl-controller/versions)
Make sure to check which controller versions are [available](https://github.com/srl-labs/srl-controller/pkgs/container/srl-controller/versions).

## Uninstall

To uninstall the controller from the cluster:
```

```text
kubectl delete -k https://github.com/srl-labs/srl-controller/config/default
```

## Testing with `kind`
To run this controller in a test cluster deployed with [`kind`](https://kind.sigs.k8s.io/) follow the steps outlined below.

1. Install `kind`
2. Clone and enter into [openconfig/kne](https://github.com/openconfig/kne) repo.
3. Build the kne cli with
`cd kne_cli && go build -o kne && chmod +x ./kne && mv ./kne /usr/local/bin`
4. deploy kind cluster and the necessary CNI with `kne deploy deploy/kne/kind.yaml` where the path to `kind.yaml` is a relative path from the root of the kne repo.
## Testing with `kne` & `kind`

This will install the `kind` cluster named `kne` with [`meshnet-cni`](https://github.com/networkop/meshnet-cni) and [`metallb`](https://metallb.universe.tf/).

A quick test may be performed to verify that SR Linux pods can communicate over the newly deployed cluster:

```bash
# apply this manifest https://gist.github.com/hellt/43cfade6178be32ea7dfa5cb64715822
# which has two srlinux pods and two linux pods
kubectl apply -f https://gist.githubusercontent.com/hellt/43cfade6178be32ea7dfa5cb64715822/raw/847a10a57dca996432be7c4a9743c0e0c5b75814/srl.yml

# once the pods are deployed and running, verify that srlinux pods can reach each other with ssh
# check what IP the srl2 has
kubectl exec -it srl1 -- ip netns exec srbase-mgmt ssh [email protected]
[email protected]'s password:
Last login: Fri Sep 24 13:58:05 2021 from 10.244.0.6
Using configuration file(s): ['/etc/opt/srlinux/srlinux.rc']
Welcome to the srlinux CLI.
Type 'help' (and press <ENTER>) if you need any help using this.
--{ [FACTORY] running }--[ ]--
```
To run this controller in a test cluster deployed with [`kne`](https://github.com/openconfig/kne) and [`kind`](https://kind.sigs.k8s.io/) follow the steps outlined in the [KNE repository](https://github.com/openconfig/kne/tree/main/docs).

If all works as expected a [demo topology with three SR Linux nodes](https://github.com/openconfig/kne/blob/main/examples/3node-srl.pb.txt) may be deployed as follows:
Once the kne+kind cluster is created, a [demo topology with two SR Linux nodes](https://github.com/openconfig/kne/blob/db5fe5be01a1b6b65bd79e740e2c819c5aeb50b0/examples/srlinux/2node-srl-with-config.pbtxt) may be deployed as follows:

```bash
kne create ~/kne/examples/3node-srl.pb.txt
kne create examples/srlinux/2node-srl-with-config.pbtxt
```

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:

```
```text
❯ kubectl -n 3node-srlinux get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-r1 LoadBalancer 10.96.151.84 172.19.0.50 57400:30006/TCP,443:30004/TCP,22:30005/TCP 6m10s
service-r2 LoadBalancer 10.96.34.36 172.19.0.51 443:30010/TCP,22:30011/TCP,57400:30009/TCP 6m9s
service-r3 LoadBalancer 10.96.159.220 172.19.0.52 443:30015/TCP,22:30016/TCP,57400:30014/TCP 6m9s
```

To connect with SSH to the `r1` node, use `ssh [email protected]` command.

### Loading images to kind cluster

[Public SR Linux container image](https://github.com/nokia/srlinux-container-image) will be pulled by kind automatically, if Internet access is present. Images which are not available publicy can be uploaded to kind manually:

```bash
# default kne kind cluster name is `kne`
# which is the last argument in the command
kind load docker-image srlinux:0.0.0-38566 --name kne
```

## Using license files

To remove the packets-per-second limit of a public container image or to launch chassis-based variants of SR Linux (ixr-6e/10e) KNE users should provide a valid license file to the `srl-controller`.

Navigate to [Using license files](docs/using-licenses.md) document to have a detailed explanation on that topic.

## Controller operations

The controller is designed to manage the `Srlinux` custom resource defined with [the following CRD](https://doc.crds.dev/github.com/srl-labs/srl-controller).

The request to create/delete a resource of kind `Srlinux` is typically coming from `openconfig/kne` topology.

### Creation
When a request to create an `Srlinux` resource named `r1` in namespace `ns` comes in, the controller's reconcile loop does the following:

1. Checks if the pods exist within a namespace `ns` with a name `r1`
When a request to create a `Srlinux` resource named `r1` in namespace `ns` comes in, the controller's reconcile loop does the following:

1. Checks if the pods exist within a namespace `ns` with a name `r1`
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.
3. When config maps are sorted out, the controller schedules a pod with the name `r1` and requeue the request
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.

### Deletion

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

### API access

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.

## Building srl-controller container image
## Building `srl-controller` container image

To build `srl-controller` container image execute:

```bash
# don't forget to set the correct tag
make docker-build IMG=ghcr.io/srl-labs/srl-controller:${tag}
```

Then upload the image to the registry.
Then upload the image to the registry and update the controller version in [manager/kustomization.yaml](config/manager/kustomization.yaml).
39 changes: 39 additions & 0 deletions api/types/v1alpha1/srl_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package v1alpha1

import (
"errors"
"regexp"
"strings"
)

// ErrVersionParse is an error which is raised when srlinux version is failed to parse.
var ErrVersionParse = errors.New("version parsing failed")

// SrlVersion represents an sr linux version as a set of fields.
type SrlVersion struct {
Major string `json:"major,omitempty"`
Minor string `json:"minor,omitempty"`
Patch string `json:"patch,omitempty"`
Build string `json:"build,omitempty"`
Commit string `json:"commit,omitempty"`
}

func parseVersionString(s string) (*SrlVersion, error) {
// for latest or missing tag we consider the version to be an engineering build
// with major = 0
if strings.ToLower(s) == "latest" || s == "" {
return &SrlVersion{"0", "", "", "", ""}, nil
}

// https://regex101.com/r/eWS6Ms/1
re := regexp.MustCompile(
`^v?(?P<major>\d{1,3})\.(?P<minor>\d{1,2})\.?(?P<patch>\d{1,2})?-?(?P<build>\d{1,10})?-?(?P<commit>\S+)?`,
)

v := re.FindStringSubmatch(s)
if v == nil {
return nil, ErrVersionParse
}

return &SrlVersion{v[1], v[2], v[3], v[4], v[5]}, nil
}
98 changes: 98 additions & 0 deletions api/types/v1alpha1/srl_version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package v1alpha1

import (
"errors"
"testing"

"github.com/google/go-cmp/cmp"
)

func TestParseVersionString(t *testing.T) {
tests := []struct {
desc string
got string
want *SrlVersion
err error
}{
{
desc: "maj, minor, patch",
got: "21.6.4",
want: &SrlVersion{"21", "6", "4", "", ""},
},
{
desc: "maj, minor",
got: "21.6",
want: &SrlVersion{"21", "6", "", "", ""},
},
{
desc: "maj, minor and extra string",
got: "21.6-test",
want: &SrlVersion{"21", "6", "", "", "test"},
},
{
desc: "maj, minor, patch and extra string",
got: "21.6.11-test",
want: &SrlVersion{"21", "6", "11", "", "test"},
},
{
desc: "maj, minor, patch, build and extra string",
got: "21.6.11-235-test",
want: &SrlVersion{"21", "6", "11", "235", "test"},
},
{
desc: "maj, minor, patch and build",
got: "21.6.11-235",
want: &SrlVersion{"21", "6", "11", "235", ""},
},
{
desc: "0.0",
got: "0.0",
want: &SrlVersion{"0", "0", "", "", ""},
},
{
desc: "0.0.0",
got: "0.0.0",
want: &SrlVersion{"0", "0", "0", "", ""},
},
{
desc: "0.0.0-34652",
got: "0.0.0-34652",
want: &SrlVersion{"0", "0", "0", "34652", ""},
},
{
desc: "latest",
got: "latest",
want: &SrlVersion{"0", "", "", "", ""},
},
{
desc: "empty",
got: "",
want: &SrlVersion{"0", "", "", "", ""},
},
{
desc: "invalid1",
got: "abcd",
want: nil,
err: ErrVersionParse,
},
}

for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
ver, err := parseVersionString(tt.got)
if !errors.Is(err, tt.err) {
t.Fatalf("got error '%v' but expected '%v'", err, tt.err)
}

if !cmp.Equal(ver, tt.want) {
t.Fatalf(
"%s: actual and expected inputs do not match\nactual: %+v\nexpected:%+v",
tt.desc,
ver,
tt.want,
)
}
},
)
}
}
Loading

0 comments on commit ec1c446

Please sign in to comment.