Commit e4ae50ad authored by Manuel Kieweg's avatar Manuel Kieweg 🤷
Browse files

Merge branch 'containerlab-ci' into 'master'

Containerlab ci

See merge request !185
parents ac2bbb70 c0f3929e
variables:
SECURE_ANALYZERS_PREFIX: registry.gitlab.com/gitlab-org/security-products/analyzers
DOCKER_IMAGE_SHA: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
DOCKER_IMAGE_SHA: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_PIPELINE_SOURCE == "schedule"'
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH != "develop" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
when: never
- if: '$CI_COMMIT_BRANCH'
stages:
- .pre
- test
- build
- test
- apply
- integration-test
- deploy
......@@ -20,5 +28,7 @@ include:
- local: '/build/ci/.security-and-compliance-ci.yml'
- local: '/build/ci/.build-container.yml'
- local: '/build/ci/.test.yml'
- local: '/build/ci/.terraform-ci.yml'
- local: '/build/ci/.deploy-k8s.yml'
\ No newline at end of file
- local: '/build/ci/.containerlab-ci.yml'
- local: '/build/ci/.uml-autogen-ci.yml'
- local: '/build/ci/.deploy-k8s.yml'
FROM golang:1.16-buster AS builder
FROM golang:1.16-buster AS installer
ARG GITLAB_USER
ARG GITLAB_TOKEN
ARG BUILDARGS
WORKDIR /src/gosdn
COPY . .
RUN apt-get update && apt-get install -y git
RUN git config --global url."https://$GITLAB_USER:$GITLAB_TOKEN@code.fbi.h-da.de".insteadOf "https://code.fbi.h-da.de"
RUN go env -w GOPRIVATE=code.fbi.h-da.de/*
WORKDIR /src/gosdn
COPY go.* ./
RUN go mod download
FROM installer as builder
COPY ./api ./api
COPY ./build ./build
COPY ./cmd ./cmd
COPY ./cli ./cli
COPY ./configs ./configs
COPY ./database ./database
COPY ./forks ./forks
COPY ./mocks ./mocks
COPY ./nucleus ./nucleus
COPY ./test ./test
RUN GOOS=linux go build $BUILDARGS ./cmd/gosdn
FROM debian:latest
EXPOSE 8080
EXPOSE 55055
COPY --from=builder /src/gosdn/gosdn .
COPY --from=builder /src/gosdn/configs ./configs
COPY ./configs ./configs
ENTRYPOINT [ "./gosdn" ]
CMD [""]
# goSDN [![coverage report](https://code.fbi.h-da.de/cocsn/gosdn/badges/master/coverage.svg)](https://code.fbi.h-da.de/cocsn/gosdn/-/commits/master) [![pipeline status](https://code.fbi.h-da.de/cocsn/gosdn/badges/master/pipeline.svg)](https://code.fbi.h-da.de/cocsn/gosdn/-/commits/master)
# goSDN [![coverage report](https://code.fbi.h-da.de/danet/gosdn/badges/master/coverage.svg)](https://code.fbi.h-da.de/danet/gosdn/-/commits/master) [![pipeline status](https://code.fbi.h-da.de/danet/gosdn/badges/master/pipeline.svg)](https://code.fbi.h-da.de/danet/gosdn/-/commits/master)
`goSDN` is a prototypical approach to build a model driven multi-vendor SDN controller.
......@@ -42,7 +42,7 @@ You can install the latest release of `goSDN` locally using the `go get` command
```sh
> git config --global url."git@code.fbi.h-da.de:".insteadOf "https://code.fbi.h-da.de"
> go env -w GOPRIVATE=code.fbi.h-da.de/cocsn/*
> go get code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn
> go get code.fbi.h-da.de/danet/gosdn/cmd/gosdn
```
To install the development version you need to clone the repo and use `go install` to build and install the binary:
......@@ -54,7 +54,7 @@ To install the development version you need to clone the repo and use `go instal
> git clone git@code.fbi.h-da.de:cocsn/gosdn.git
# checkout the develop branch (or any other branch)
> cd $GOPATH/src/code.fbi.h-da.de/cocsn/gosdn
> cd $GOPATH/src/code.fbi.h-da.de/danet/gosdn
> git checkout develop
> go install ./cmd/gosdn
```
......@@ -64,7 +64,7 @@ Now you can start `goSDN` locally using the `gosdn` command or [use the CLI](#us
# Getting Started
## k8s
We have an instance of `goSDN` for each the latest master and current develop branch running on the department's k8s cluster. These endpoints can be accessed using the `gosdn cli` command. If anything breaks please file an [issue](https://code.fbi.h-da.de/cocsn/gosdn/-/issues/new).
We have an instance of `goSDN` for each the latest master and current develop branch running on the department's k8s cluster. These endpoints can be accessed using the `gosdn cli` command. If anything breaks please file an [issue](https://code.fbi.h-da.de/danet/gosdn/-/issues/new).
## Using the CLI
......@@ -78,5 +78,5 @@ The `gosdn cli` command allows you to interact with a running `goSDN` controller
| Master | Develop |
| ------ | ------ |
| [![coverage report](https://code.fbi.h-da.de/cocsn/gosdn/badges/master/coverage.svg)](https://code.fbi.h-da.de/cocsn/gosdn/-/commits/master) | [![coverage report](https://code.fbi.h-da.de/cocsn/gosdn/badges/develop/coverage.svg)](https://code.fbi.h-da.de/cocsn/gosdn/-/commits/develop) |
| [![pipeline status](https://code.fbi.h-da.de/cocsn/gosdn/badges/master/pipeline.svg)](https://code.fbi.h-da.de/cocsn/gosdn/-/commits/master) | [![pipeline status](https://code.fbi.h-da.de/cocsn/gosdn/badges/develop/pipeline.svg)](https://code.fbi.h-da.de/cocsn/gosdn/-/commits/develop) |
| [![coverage report](https://code.fbi.h-da.de/danet/gosdn/badges/master/coverage.svg)](https://code.fbi.h-da.de/danet/gosdn/-/commits/master) | [![coverage report](https://code.fbi.h-da.de/danet/gosdn/badges/develop/coverage.svg)](https://code.fbi.h-da.de/danet/gosdn/-/commits/develop) |
| [![pipeline status](https://code.fbi.h-da.de/danet/gosdn/badges/master/pipeline.svg)](https://code.fbi.h-da.de/danet/gosdn/-/commits/master) | [![pipeline status](https://code.fbi.h-da.de/danet/gosdn/badges/develop/pipeline.svg)](https://code.fbi.h-da.de/danet/gosdn/-/commits/develop) |
......@@ -8,7 +8,7 @@
syntax = "proto3";
package openconfig.enums;
option go_package = "code.fbi.h-da.de/cocsn/gosdn";
option go_package = "code.fbi.h-da.de/danet/gosdn";
import "github.com/openconfig/ygot/proto/ywrapper/ywrapper.proto";
import "github.com/openconfig/ygot/proto/yext/yext.proto";
......
......@@ -15,7 +15,7 @@
package openconfig
import (
openconfig_interfaces "code.fbi.h-da.de/cocsn/gosdn/api/proto/openconfig/openconfig_interfaces"
openconfig_interfaces "code.fbi.h-da.de/danet/gosdn/api/proto/openconfig/openconfig_interfaces"
proto "github.com/golang/protobuf/proto"
_ "github.com/openconfig/ygot/proto/yext"
_ "github.com/openconfig/ygot/proto/ywrapper"
......
......@@ -8,7 +8,7 @@
syntax = "proto3";
package openconfig;
option go_package = "code.fbi.h-da.de/cocsn/gosdn";
option go_package = "code.fbi.h-da.de/danet/gosdn";
import "github.com/openconfig/ygot/proto/ywrapper/ywrapper.proto";
......
......@@ -15,7 +15,7 @@
package openconfig_openconfig_interfaces
import (
enums "code.fbi.h-da.de/cocsn/gosdn/api/proto/openconfig/enums"
enums "code.fbi.h-da.de/danet/gosdn/api/proto/openconfig/enums"
proto "github.com/golang/protobuf/proto"
_ "github.com/openconfig/ygot/proto/yext"
ywrapper "github.com/openconfig/ygot/proto/ywrapper"
......
......@@ -8,7 +8,7 @@
syntax = "proto3";
package openconfig.openconfig_interfaces;
option go_package = "code.fbi.h-da.de/cocsn/gosdn";
option go_package = "code.fbi.h-da.de/danet/gosdn";
import "github.com/openconfig/ygot/proto/ywrapper/ywrapper.proto";
import "github.com/openconfig/ygot/proto/yext/yext.proto";
......
......@@ -3,12 +3,13 @@ package main
import (
"context"
"os"
"strconv"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"code.fbi.h-da.de/danet/gosdn/nucleus"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
netv1 "k8s.io/api/networking/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
......@@ -16,6 +17,16 @@ import (
"k8s.io/client-go/tools/clientcmd"
)
// GRPC defines the grpc port used by the deployment
const GRPC = 55055
// HTTP defines the http port used by the deployment
const HTTP = 8080
var service *corev1.Service
var deployment *appv1.Deployment
var config *corev1.ConfigMap
func main() {
log.SetFormatter(&log.JSONFormatter{})
kubeconfig, err := clientcmd.BuildConfigFromFlags("https://api.ocp.fbi.h-da.de:6443", "")
......@@ -37,15 +48,6 @@ func main() {
tag = os.Getenv("CI_COMMIT_SHA")
}
switch os.Getenv("CI_NIGHTLY") {
case "mainline":
tag = "nightly"
case "develop":
tag = "nightly-develop"
default:
tag = os.Getenv("CI_COMMIT_SHA")
}
switch os.Getenv("K8S_OP") {
case "create":
if err := create(clientset, tag); err != nil {
......@@ -55,27 +57,56 @@ func main() {
if err := remove(clientset, tag); err != nil {
log.Fatal(err)
}
case "getenv":
if err := getenv(clientset, tag); err != nil {
log.Fatal(err)
}
default:
log.Fatal("invalid option")
}
}
func getenv(clientset *kubernetes.Clientset, tag string) error {
env := "gosdn-" + tag
opts := metav1.GetOptions{}
ctx := context.Background()
service, err := clientset.CoreV1().Services("cocsn").Get(ctx, env, opts)
if err != nil {
return err
}
var ip string
for _, ingress := range service.Status.LoadBalancer.Ingress {
ip = ingress.IP
}
var port string
for _, p := range service.Spec.Ports {
if p.Name == "grpc" {
port = strconv.Itoa(int(p.NodePort))
}
}
log.WithFields(log.Fields{
"ip": ip,
"port": port,
}).Info(env)
viper.Set("GOSDN_TEST_API_ENDPOINT", ip+":"+port)
return viper.SafeWriteConfigAs(".k8s.toml")
}
// nolint
func create(clientset *kubernetes.Clientset, tag string) error {
env := "gosdn-" + tag
service := createService(env)
ingress := createIngress(env)
config := createConfigMap(env)
deployment := createDeployment(env, tag)
deployment = createDeployment(env, tag)
opts := metav1.CreateOptions{}
ctx := context.Background()
_, err := clientset.CoreV1().Services("cocsn").Create(ctx, service, opts)
_, err := clientset.AppsV1().Deployments("cocsn").Create(ctx, deployment, opts)
if err != nil {
switch err.(type) {
case *errors.StatusError:
if err.(*errors.StatusError).ErrStatus.Code == 409 {
if err := update(clientset, service, env); err != nil {
return err
if err := update(clientset, deployment, env); err != nil {
log.Error(err)
}
} else {
log.Error(err)
......@@ -84,25 +115,14 @@ func create(clientset *kubernetes.Clientset, tag string) error {
log.Error(err)
}
} else {
log.Printf("service %v created", service.Name)
log.Printf("deployment %v created", deployment.Name)
}
_, err = clientset.NetworkingV1beta1().Ingresses("cocsn").Create(ctx, ingress, opts)
deployment, err = clientset.AppsV1().Deployments("cocsn").Get(ctx, env, metav1.GetOptions{})
if err != nil {
switch err.(type) {
case *errors.StatusError:
if err.(*errors.StatusError).ErrStatus.Code == 409 {
if err := update(clientset, ingress, env); err != nil {
log.Error(err)
}
} else {
log.Error(err)
}
default:
log.Error(err)
}
} else {
log.Printf("ingress %v created", ingress.Name)
log.Error(err)
}
config = createConfigMap(env)
_, err = clientset.CoreV1().ConfigMaps("cocsn").Create(ctx, config, opts)
if err != nil {
switch err.(type) {
......@@ -120,13 +140,15 @@ func create(clientset *kubernetes.Clientset, tag string) error {
} else {
log.Printf("configMap %v created", config.Name)
}
_, err = clientset.AppsV1().Deployments("cocsn").Create(ctx, deployment, opts)
service = createService(env)
_, err = clientset.CoreV1().Services("cocsn").Create(ctx, service, opts)
if err != nil {
switch err.(type) {
case *errors.StatusError:
if err.(*errors.StatusError).ErrStatus.Code == 409 {
if err := update(clientset, deployment, env); err != nil {
log.Error(err)
if err := update(clientset, service, env); err != nil {
return err
}
} else {
log.Error(err)
......@@ -135,7 +157,7 @@ func create(clientset *kubernetes.Clientset, tag string) error {
log.Error(err)
}
} else {
log.Printf("deployment %v created", deployment.Name)
log.Printf("service %v created", service.Name)
}
return nil
}
......@@ -144,9 +166,9 @@ func update(clientset *kubernetes.Clientset, resource metav1.Common, env string)
opts := metav1.UpdateOptions{}
getOpts := metav1.GetOptions{}
ctx := context.Background()
switch resource.(type) {
switch resource := resource.(type) {
case *corev1.Service:
service := resource.(*corev1.Service)
service := resource
s, err := clientset.CoreV1().Services("cocsn").Get(ctx, env, getOpts)
if err != nil {
return err
......@@ -157,20 +179,8 @@ func update(clientset *kubernetes.Clientset, resource metav1.Common, env string)
return err
}
log.Printf("service %v updated", service.Name)
case *netv1.Ingress:
ingress := resource.(*netv1.Ingress)
i, err := clientset.NetworkingV1beta1().Ingresses("cocsn").Get(ctx, env, getOpts)
if err != nil {
return err
}
i.DeepCopyInto(ingress)
_, err = clientset.NetworkingV1beta1().Ingresses("cocsn").Update(ctx, ingress, opts)
if err != nil {
return err
}
log.Printf("ingress %v updated", ingress.Name)
case *corev1.ConfigMap:
config := resource.(*corev1.ConfigMap)
config := resource
c, err := clientset.CoreV1().ConfigMaps("cocsn").Get(ctx, env+"-config", getOpts)
if err != nil {
return err
......@@ -182,7 +192,7 @@ func update(clientset *kubernetes.Clientset, resource metav1.Common, env string)
}
log.Printf("configMap %v updated", config.Name)
case *appv1.Deployment:
deployment := resource.(*appv1.Deployment)
deployment := resource
d, err := clientset.AppsV1().Deployments("cocsn").Get(ctx, env, getOpts)
if err != nil {
return err
......@@ -221,12 +231,6 @@ func remove(clientset *kubernetes.Clientset, tag string) error {
} else {
log.Printf("deployment %v deleted", env)
}
err = clientset.NetworkingV1beta1().Ingresses("cocsn").Delete(ctx, env, opts)
if err != nil {
log.Error(err)
} else {
log.Printf("ingress %v deleted", env)
}
return err
}
......@@ -240,23 +244,30 @@ func createService(environment string) *corev1.Service {
Name: environment,
Namespace: "cocsn",
Labels: map[string]string{"run": environment},
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "Deployment",
Name: environment,
UID: deployment.GetUID(),
},
},
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "http",
Port: 8080,
TargetPort: intstr.IntOrString{IntVal: 8080},
Port: HTTP,
TargetPort: intstr.IntOrString{IntVal: HTTP},
},
{
Name: "grpc",
Port: 55055,
TargetPort: intstr.IntOrString{IntVal: 55055},
Port: GRPC,
TargetPort: intstr.IntOrString{IntVal: GRPC},
},
},
Selector: map[string]string{"run": environment},
Type: "NodePort",
ExternalName: environment + ".apps.ocp.fbi.h-da.de",
Selector: map[string]string{"run": environment},
Type: "LoadBalancer",
},
}
}
......@@ -294,18 +305,18 @@ func createDeployment(environment, hash string) *appv1.Deployment {
Containers: []corev1.Container{
{
Name: "gosdn",
Image: "registry.code.fbi.h-da.de/cocsn/gosdn:" + hash,
Image: "registry.code.fbi.h-da.de/danet/gosdn:" + hash,
Command: nil,
Args: nil,
WorkingDir: "",
Ports: []corev1.ContainerPort{
{
Name: "grpc",
ContainerPort: 55055,
ContainerPort: GRPC,
},
{
Name: "http",
ContainerPort: 8080,
ContainerPort: HTTP,
},
},
VolumeMounts: []corev1.VolumeMount{
......@@ -318,7 +329,7 @@ func createDeployment(environment, hash string) *appv1.Deployment {
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/livez",
Port: intstr.IntOrString{IntVal: 8080},
Port: intstr.IntOrString{IntVal: HTTP},
},
},
InitialDelaySeconds: 5,
......@@ -328,7 +339,7 @@ func createDeployment(environment, hash string) *appv1.Deployment {
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/readyz",
Port: intstr.IntOrString{IntVal: 8080},
Port: intstr.IntOrString{IntVal: HTTP},
},
},
InitialDelaySeconds: 10,
......@@ -358,41 +369,15 @@ func createConfigMap(env string) *corev1.ConfigMap {
},
ObjectMeta: metav1.ObjectMeta{
Name: env + "-config",
},
Data: map[string]string{"gosdn.toml": "#empty"},
}
}
func createIngress(env string) *netv1.Ingress {
return &netv1.Ingress{
TypeMeta: metav1.TypeMeta{
Kind: "Ingress",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: env,
Namespace: "cocsn",
},
Spec: netv1.IngressSpec{
Rules: []netv1.IngressRule{
OwnerReferences: []metav1.OwnerReference{
{
Host: env + ".apps.ocp.fbi.h-da.de",
IngressRuleValue: netv1.IngressRuleValue{
HTTP: &netv1.HTTPIngressRuleValue{
Paths: []netv1.HTTPIngressPath{
{
Path: "/api",
Backend: netv1.IngressBackend{
ServiceName: env,
ServicePort: intstr.IntOrString{IntVal: 8080},
},
},
},
},
},
APIVersion: "v1",
Kind: "Deployment",
Name: env,
UID: deployment.GetUID(),
},
},
},
Status: netv1.IngressStatus{},
Data: map[string]string{"gosdn.toml": "#empty"},
}
}
services:
- docker:19.03.12-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_IMAGE_SHA: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
.build: &build
build-docker:
before_script:
- echo "override global before script"
image: docker:19.03.12
stage: build
allow_failure: false
needs: []
tags:
- dind
script:
- >
docker build \
--build-arg GITLAB_USER=$GO_MODULES_USER \
--build-arg GITLAB_TOKEN=$GO_MODULES_ACCESS_TOKEN \
--build-arg BUILDARGS=$BUILDARGS \
-t $DOCKER_IMAGE_SHA .
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker push $DOCKER_IMAGE_SHA
- docker tag $DOCKER_IMAGE_SHA $TAG
- docker push $TAG
- shell-builder
build:develop:
variables:
TAG: $CI_REGISTRY_IMAGE:develop
BUILDARGS: -race
rules:
- if: $CI_COMMIT_BRANCH == "develop" && $CI_NIGHTLY == null
<<: *build
build:nightly:develop:
variables:
TAG: $CI_REGISTRY_IMAGE:nightly-develop
BUILDARGS: -race
rules:
variables:
TAG: $CI_REGISTRY_IMAGE:develop
BUILDARGS: -race
- if: $CI_NIGHTLY == "develop"
<<: *build
build:nightly:
variables:
TAG: $CI_REGISTRY_IMAGE:nightly
rules:
variables:
TAG: $CI_REGISTRY_IMAGE:nightly-develop
BUILDARGS: -race
- if: $CI_NIGHTLY == "mainline"
<<: *build
build:merge-request:
variables:
TAG: $CI_REGISTRY_IMAGE:merge-request
BUILDARGS: -race
rules:
variables:
TAG: $CI_REGISTRY_IMAGE:nightly
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
<<: *build
build:latest:
variables:
TAG: $CI_REGISTRY_IMAGE:latest
rules:
variables:
TAG: $CI_REGISTRY_IMAGE:merge-request
BUILDARGS: -race
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_NIGHTLY == null
<<: *build
\ No newline at end of file
variables:
TAG: $CI_REGISTRY_IMAGE:latest
- if: '$CI_COMMIT_BRANCH'
variables:
TAG: $CI_REGISTRY_IMAGE:branch
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build --build-arg GITLAB_USER=$GO_MODULES_USER --build-arg GITLAB_TOKEN=$GO_MODULES_ACCESS_TOKEN --build-arg BUILDARGS=$BUILDARGS -t $DOCKER_IMAGE_SHA .
- docker push $DOCKER_IMAGE_SHA
- docker tag $DOCKER_IMAGE_SHA $TAG
- docker push $TAG
- docker build --target installer --build-arg GITLAB_USER=$GO_MODULES_USER --build-arg GITLAB_TOKEN=$GO_MODULES_ACCESS_TOKEN --build-arg BUILDARGS=$BUILDARGS -t ${CI_REGISTRY_IMAGE}:testing_${CI_PIPELINE_ID} .
- docker push ${CI_REGISTRY_IMAGE}:testing_${CI_PIPELINE_ID}
......@@ -12,4 +12,4 @@ code-quality:
reports:
codequality: gl-code-quality-report.json
paths:
- gl-code-quality-report.json
\ No newline at end of file
- gl-code-quality-report.json
variables:
CEOS_CONTAINER_IMAGE: "$CI_REGISTRY_IMAGE/ceos:latest"
CLAB_INT1_TEMPLATE: "${CI_PROJECT_DIR}/test/containerlab/int01.clab.yml"
CLAB_NAME: "clab${CI_PIPELINE_IID}"
CLAB_DIR: "/mnt"
.containerlab_rules: &containerlab_rules