diff --git a/build/cd/deploy.go b/build/cd/deploy.go index 121bd08a794ee8609e40131b0ee654969e470892..45b3b0998d8017bb1d1af6504bc0956eaa1fbe60 100644 --- a/build/cd/deploy.go +++ b/build/cd/deploy.go @@ -3,12 +3,13 @@ package main import ( "context" "os" + "strconv" nucleus "code.fbi.h-da.de/cocsn/gosdn/nucleus/errors" 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 } @@ -157,18 +179,6 @@ func update(clientset *kubernetes.Clientset, resource metav1.Common, env string) return err } log.Printf("service %v updated", service.Name) - case *netv1.Ingress: - ingress := resource - 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 c, err := clientset.CoreV1().ConfigMaps("cocsn").Get(ctx, env+"-config", getOpts) @@ -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,33 @@ func createService(environment string) *corev1.Service { Name: environment, Namespace: "cocsn", Labels: map[string]string{"run": environment}, + Annotations: map[string]string{ + "metallb.universe.tf/address-pool": "hdanet", + }, + 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", }, } } @@ -301,11 +315,11 @@ func createDeployment(environment, hash string) *appv1.Deployment { Ports: []corev1.ContainerPort{ { Name: "grpc", - ContainerPort: 55055, + ContainerPort: GRPC, }, { Name: "http", - ContainerPort: 8080, + ContainerPort: HTTP, }, }, VolumeMounts: []corev1.VolumeMount{ @@ -318,7 +332,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 +342,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 +372,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"}, } } diff --git a/build/ci/.build-container.yml b/build/ci/.build-container.yml index 8a092f8a3f88e0b8147052d8032219379d68e294..2035443422a42301e9af33ea999ab0c0761a3173 100644 --- a/build/ci/.build-container.yml +++ b/build/ci/.build-container.yml @@ -1,6 +1,7 @@ services: - - docker:19.03.12-dind + - name: docker:19.03.12-dind + command: ["--registry-mirror", "http://141.100.70.170:6000"] variables: DOCKER_TLS_CERTDIR: "/certs" @@ -14,6 +15,7 @@ variables: tags: - dind script: + - docker info - > docker build \ --build-arg GITLAB_USER=$GO_MODULES_USER \ diff --git a/build/ci/.deploy-k8s.yml b/build/ci/.deploy-k8s.yml index 7046b8e9242868e62a8503ad7cc81599603780b4..ca2a8d39564b39e4b5508b9d2a779e1b950e9009 100644 --- a/build/ci/.deploy-k8s.yml +++ b/build/ci/.deploy-k8s.yml @@ -14,7 +14,9 @@ build:k8s-bot: - build/cd/k8s-bot .deploy: &deploy - image: bitnami/kubectl:latest + image: + name: bitnami/kubectl:latest + entrypoint: [""] before_script: - echo "override global before script" variables: @@ -73,7 +75,9 @@ deploy:nightly:develop: - if: $CI_COMMIT_BRANCH == "develop" && $CI_NIGHTLY == "mainline" destroy:k8s: - image: bitnami/kubectl:latest + image: + name: bitnami/kubectl:latest + entrypoint: [""] rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: always diff --git a/build/ci/.test.yml b/build/ci/.test.yml index e4209b3bb7773f44ee961cfd6379ad9554645963..2d410c324e99359328a61612fcd1e52b3890a1b6 100644 --- a/build/ci/.test.yml +++ b/build/ci/.test.yml @@ -1,12 +1,9 @@ -integration-test: +.integration-test: &integration-test image: golang:1.16 stage: integration-test needs: - job: "apply" - job: "deploy:integration-test" - variables: - GOSDN_LOG: "nolog" - GOSDN_TEST_API_ENDPOINT: http://gosdn-$CI_COMMIT_SHA.apps.ocp.fbi.h-da.de/api rules: - if: $CI_NIGHTLY when: delayed @@ -18,8 +15,36 @@ integration-test: allow_failure: true when: delayed start_in: 2 minutes + +integration-test:nucleus: + <<: *integration-test + script: + - cd ./test/integration + - go test -race -v -run TestGnmi_SetIntegration + - go test -race -v -run TestGnmi_GetIntegration + - go test -race -v -run TestGnmi_SubscribeIntegration + - go test -race -v -run TestGnmi_CapabilitiesIntegration + +integration-test:cmd: + <<: *integration-test + variables: + K8S_OP: "getenv" + needs: + - job: "build:merge-request" + - job: "apply" + - job: "deploy:integration-test" + - job: "build:k8s-bot" + artifacts: true script: - - go test -race ./test/integration -v -coverprofile=coverage.out + - cd ./test/integration + - ./../../build/cd/k8s-bot + - go test -race -v -run TestCmdIntegration + +integration-test:cli: + <<: *integration-test + script: + - cd ./test/integration + - go test -race -v -run TestCapabilities .test: &test image: golang:1.16 @@ -45,4 +70,4 @@ unit-test: controller-test: script: - go test -race -v -run TestRun - <<: *test \ No newline at end of file + <<: *test diff --git a/cli/capabilities.go b/cli/capabilities.go index 9e3518f82b48d0361754e27d2e6d7a9ed879423b..f6cc702a1ed43e9cf92398ac39c51ee5c946555c 100644 --- a/cli/capabilities.go +++ b/cli/capabilities.go @@ -19,6 +19,9 @@ func Capabilities(a, u, p string) error { Address: a, Username: u, Password: p, + TransportOption: &tpb.TransportOption_GnmiTransportOption{ + GnmiTransportOption: &tpb.GnmiTransportOption{}, + }, } transport, err := nucleus.NewGnmiTransport(opts, nucleus.NewSBI(spb.Type_OPENCONFIG)) if err != nil { diff --git a/controller.go b/controller.go index c6a434178c9cf4b1c15c46e1d9c8ff4bb2d06289..0ad936e2c18056e6252886cdc9434870a8d6ce3f 100644 --- a/controller.go +++ b/controller.go @@ -69,6 +69,7 @@ func startGrpcServer() error { if err != nil { return err } + log.Infof("listening to %v", lis.Addr()) c.grpcServer = grpc.NewServer() c.nbi = nbi.NewNBI(c.pndc) pb.RegisterCoreServer(c.grpcServer, c.nbi.Core) diff --git a/go.mod b/go.mod index 883bcc2b4a4f41c5d16d45b27701bd45f6ab48df..44c98cec995d52ea7e3eec1e001b23c192fc43c8 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( code.fbi.h-da.de/cocsn/api/go v0.0.0-20210528163424-f37e91dc5895 code.fbi.h-da.de/cocsn/yang-models v0.0.7 github.com/aristanetworks/goarista v0.0.0-20201120222254-94a892eb0c6a - github.com/golang/protobuf v1.5.2 github.com/docker/docker v1.13.1 + github.com/golang/protobuf v1.5.2 github.com/google/gnxi v0.0.0-20201221102247-c26672548161 github.com/google/uuid v1.1.2 github.com/neo4j/neo4j-go-driver v1.8.3 diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go index 1834ce7e13e9acd6e5bf2ea7871b960f01bf3d98..57fa046234bbbcf40b2911cb58c6bc035746e4d9 100644 --- a/nucleus/gnmi_transport.go +++ b/nucleus/gnmi_transport.go @@ -43,20 +43,18 @@ type Gnmi struct { // transport based on the values of it. func NewGnmiTransport(opts *tpb.TransportOption, sbi SouthboundInterface) (*Gnmi, error) { gnmiConfig := &gnmi.Config{ - Addr: opts.Address, - Password: opts.Password, - Username: opts.Username, - TLS: opts.Tls, - Compression: opts.GetGnmiTransportOption().Compression, + Addr: opts.Address, + Password: opts.Password, + Username: opts.Username, + TLS: opts.Tls, } c, err := gnmi.Dial(gnmiConfig) if err != nil { return nil, err } log.WithFields(log.Fields{ - "target": opts.Address, - "tls": opts.Tls, - "encoding": opts.GetGnmiTransportOption().Encoding, + "target": opts.Address, + "tls": opts.Tls, }).Info("building new gNMI transport") return &Gnmi{ SetNode: sbi.SetNode(), diff --git a/test/integration/cmdIntegration_test.go b/test/integration/cmdIntegration_test.go index dd24e37caa1144b200e1aa47b9bc6f97ae77c298..b7667640f65dae3999efd53beae6ce2e958775bb 100644 --- a/test/integration/cmdIntegration_test.go +++ b/test/integration/cmdIntegration_test.go @@ -19,7 +19,7 @@ const unreachable = "203.0.113.10:6030" const testPath = "/system/config/hostname" var testAddress = "141.100.70.171:6030" -var testAPIEndpoint = "http://gosdn-latest.apps.ocp.fbi.h-da.de/api" +var testAPIEndpoint = "gosdn-latest.apps.ocp.fbi.h-da.de:55055" var testUsername = "admin" var testPassword = "arista" var opt *tpb.TransportOption @@ -30,6 +30,11 @@ func TestMain(m *testing.M) { } func testSetupIntegration() { + viper.SetConfigFile(".k8s.toml") + if err := viper.ReadInConfig(); err != nil { + log.Error(err) + } + if os.Getenv("GOSDN_LOG") == "nolog" { log.SetLevel(log.PanicLevel) } @@ -39,7 +44,7 @@ func testSetupIntegration() { testAddress = a log.Infof("GOSDN_TEST_ENDPOINT set to %v", testAddress) } - api := os.Getenv("GOSDN_TEST_API_ENDPOINT") + api := viper.GetString("gosdn_test_api_endpoint") if api != "" { testAPIEndpoint = api log.Infof("GOSDN_TEST_API_ENDPOINT set to %v", testAPIEndpoint) diff --git a/test/integration/nucleusIntegration_test.go b/test/integration/nucleusIntegration_test.go index 4a5a29eebeb8d7acb0c0fdf7310314b83000694a..8a4d1205e21b502131e8b03702ad0da04d71685b 100644 --- a/test/integration/nucleusIntegration_test.go +++ b/test/integration/nucleusIntegration_test.go @@ -172,9 +172,9 @@ func TestGnmi_SubscribeIntegration(t *testing.T) { name: "default", fields: fields{ opt: &tpb.TransportOption{ - Address: "", - Username: "", - Password: "", + Address: testAddress, + Username: testUsername, + Password: testPassword, Tls: false, TransportOption: &tpb.TransportOption_GnmiTransportOption{ GnmiTransportOption: &tpb.GnmiTransportOption{ @@ -250,7 +250,7 @@ func TestGnmi_SubscribeIntegration(t *testing.T) { if (subErr != nil) != wantErr { if !wantErr && subErr != nil { if subErr.Error() != "rpc error: code = Canceled desc = context canceled" { - t.Errorf("Subscribe() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Subscribe() error = %v, wantErr %v", subErr, tt.wantErr) } } }