diff --git a/test/integration/cmdIntegration_test.go b/api/apiIntegration_test.go similarity index 87% rename from test/integration/cmdIntegration_test.go rename to api/apiIntegration_test.go index 59a30290197c80bd4054918a64be145dfa25efef..35475bc98c6e6ed305b117854946f8e41a36f0d2 100644 --- a/test/integration/cmdIntegration_test.go +++ b/api/apiIntegration_test.go @@ -1,17 +1,17 @@ -package integration +package api import ( + "fmt" + "os" + "testing" + tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport" - "code.fbi.h-da.de/cocsn/gosdn/api" "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto" - "fmt" guuid "github.com/google/uuid" gpb "github.com/openconfig/gnmi/proto/gnmi" log "github.com/sirupsen/logrus" "github.com/spf13/viper" pb "google.golang.org/protobuf/proto" - "os" - "testing" ) const unreachable = "203.0.113.10:6030" @@ -20,10 +20,11 @@ const testPath = "/system/config/hostname" var testIP = "141.100.70.171" var testPort = "6030" var testAddress = testIP + ":" + testPort -var testAPIEndpoint = "http://gosdn-latest.apps.ocp.fbi.h-da.de/api" +var testAPIEndpoint = "gosdn-latest.apps.ocp.fbi.h-da.de" var testUsername = "admin" var testPassword = "arista" var opt *tpb.TransportOption +var gnmiMessages map[string]pb.Message func TestMain(m *testing.M) { testSetupIntegration() @@ -31,6 +32,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) } @@ -91,7 +97,9 @@ func testSetupIntegration() { } } -func TestCmdIntegration(t *testing.T) { +func TestApiIntegration(t *testing.T) { + // TDOO: Remove once openshift grpc support is available + t.Skip("skipped due to openshift limitations") if testing.Short() { t.Skip("skipping integration test") } @@ -107,7 +115,7 @@ func TestCmdIntegration(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer viper.Reset() - if err := api.Init(testAPIEndpoint); (err != nil) != tt.wantErr { + if err := Init(testAPIEndpoint); (err != nil) != tt.wantErr { switch err.(type) { case viper.ConfigFileNotFoundError: default: @@ -118,7 +126,7 @@ func TestCmdIntegration(t *testing.T) { cliPnd := viper.GetString("CLI_PND") cliSbi := viper.GetString("CLI_SBI") - if _, err := api.AddDevice( + if _, err := AddDevice( testAPIEndpoint, testUsername, testPassword, @@ -132,7 +140,7 @@ func TestCmdIntegration(t *testing.T) { } did := viper.GetString("LAST_DEVICE_UUID") - _, err := api.GetDevice( + _, err := GetDevice( testAPIEndpoint, cliPnd, testPath, @@ -143,7 +151,7 @@ func TestCmdIntegration(t *testing.T) { return } - _, err = api.GetDevice( + _, err = GetDevice( testAPIEndpoint, cliPnd, "", @@ -155,7 +163,7 @@ func TestCmdIntegration(t *testing.T) { } hostname := guuid.New().String() - _, err = api.Update( + _, err = Update( testAPIEndpoint, did, cliPnd, @@ -167,10 +175,10 @@ func TestCmdIntegration(t *testing.T) { return } - resp, err := api.GetDevice(testAddress, testUsername, testPassword, testPath) + resp, err := GetDevice(testAddress, testUsername, testPassword, testPath) if err != nil { if !tt.wantErr { - t.Errorf("api.Get() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr) } return } diff --git a/api/capabilities.go b/api/capabilities.go deleted file mode 100644 index 93735adf1798d761a354e312584cb5b3b2c0524e..0000000000000000000000000000000000000000 --- a/api/capabilities.go +++ /dev/null @@ -1,45 +0,0 @@ -package api - -import ( - "context" - "fmt" - "strings" - - spb "code.fbi.h-da.de/cocsn/api/go/gosdn/southbound" - tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport" - - "code.fbi.h-da.de/cocsn/gosdn/nucleus" - gpb "github.com/openconfig/gnmi/proto/gnmi" -) - -// Capabilities sends a gNMI Capabilities request to the specified target -// and prints the supported models to stdout -func Capabilities(a, u, p string) error { - opts := &tpb.TransportOption{ - Address: a, - Username: u, - Password: p, - } - transport, err := nucleus.NewGnmiTransport(opts, nucleus.NewSBI(spb.Type_OPENCONFIG)) - if err != nil { - return err - } - resp, err := transport.Capabilities(context.Background()) - if err != nil { - return err - } - modelData := resp.(*gpb.CapabilityResponse).SupportedModels - b := strings.Builder{} - for _, elem := range modelData { - _, err := b.WriteString(elem.Name) - if err != nil { - return err - } - _, err = b.WriteString("\n") - if err != nil { - return err - } - } - fmt.Println(b.String()) - return nil -} diff --git a/api/go.mod b/api/go.mod index 0e0e8f2c94ce0c188d5d000abf9d84185009d28b..e3c75264d71e73e5a04413b5717b77667865118c 100644 --- a/api/go.mod +++ b/api/go.mod @@ -8,10 +8,9 @@ require ( code.fbi.h-da.de/cocsn/yang-models v0.0.7 github.com/google/uuid v1.2.0 github.com/openconfig/gnmi v0.0.0-20210527163611-d3a3e30199da - github.com/openconfig/goyang v0.2.4 - github.com/openconfig/ygot v0.10.11 github.com/sirupsen/logrus v1.8.1 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.7.0 google.golang.org/grpc v1.37.0 + google.golang.org/protobuf v1.26.0 ) diff --git a/api/go.sum b/api/go.sum index 3054b8cd0aefee49f78fabf0040db1706546a087..697c8302ec0d6fb98a2900b54d8c6ea3c9d4bdce 100644 --- a/api/go.sum +++ b/api/go.sum @@ -171,7 +171,6 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnxi v0.0.0-20210423111716-4b504ef806a7 h1:cJ62uhbZcclaYm9gq4JNyazqSY7bUEggwZdw0nHTT7o= github.com/google/gnxi v0.0.0-20210423111716-4b504ef806a7/go.mod h1:dPTuHPVOqxZ2yGKPjymiMt1vrZa8KHXWKX+Lx1z5d88= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= diff --git a/api/subscribe.go b/api/subscribe.go deleted file mode 100644 index 7c05ebc243b51b21b62eb287b2c50e3469fdedde..0000000000000000000000000000000000000000 --- a/api/subscribe.go +++ /dev/null @@ -1,68 +0,0 @@ -package api - -import ( - "context" - "fmt" - "os" - "os/signal" - "syscall" - "time" - - tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport" - "code.fbi.h-da.de/cocsn/gosdn/nucleus/types" - - "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" - "code.fbi.h-da.de/cocsn/gosdn/nucleus" - log "github.com/sirupsen/logrus" -) - -// Subscribe starts a gNMI subscriber requersting the specified paths on the target and -// logs the response to stdout. Only 'stream' mode with 'sample' operation supported. -func Subscribe(address, username, password, deviceName string, sample, heartbeat int64, args ...string) error { - sbi := &nucleus.OpenConfig{} - tOpts := &tpb.TransportOption{ - Address: address, - Username: username, - Password: password, - Tls: false, - TransportOption: &tpb.TransportOption_GnmiTransportOption{ - GnmiTransportOption: &tpb.GnmiTransportOption{ - Compression: "", - GrpcDialOptions: nil, - Token: "", - Encoding: 0, - }, - }, - } - - device, err := nucleus.NewDevice(deviceName, tOpts, sbi) - if err != nil { - return err - } - - opts := &gnmi.SubscribeOptions{ - UpdatesOnly: false, - Prefix: "", - Mode: "stream", - StreamMode: "sample", - SampleInterval: uint64(sample * time.Second.Nanoseconds()), - SuppressRedundant: false, - HeartbeatInterval: uint64(heartbeat * time.Second.Nanoseconds()), - Paths: gnmi.SplitPaths(args), - Origin: "", - Target: address, - } - done := make(chan os.Signal, 1) - signal.Notify(done, syscall.SIGILL, syscall.SIGTERM) - ctx := context.WithValue(context.Background(), types.CtxKeyOpts, opts) //nolint - go func() { - if err := device.Transport().Subscribe(ctx); err != nil { - log.Fatal(err) - } - }() - fmt.Println("awaiting signal") - <-done - fmt.Println("exiting") - - return nil -} diff --git a/api/target.go b/api/target.go deleted file mode 100644 index abaf5c1db2aadd15464639fe5aa30acf65ea4cb3..0000000000000000000000000000000000000000 --- a/api/target.go +++ /dev/null @@ -1,96 +0,0 @@ -package api - -import ( - "context" - "net" - "reflect" - - "code.fbi.h-da.de/cocsn/gosdn/forks/google/gnmi" - oc "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" - pb "github.com/openconfig/gnmi/proto/gnmi" - "github.com/openconfig/goyang/pkg/yang" - "github.com/openconfig/ygot/util" - "github.com/openconfig/ygot/ygot" - log "github.com/sirupsen/logrus" - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" -) - -type server struct { - *gnmi.Server -} - -func callback(newConfig ygot.ValidatedGoStruct) error { - // Apply the config to your device and return nil if success. return error if fails. - // - // Do something ... - return nil -} - -func newServer(model *gnmi.Model, config []byte) (*server, error) { - s, err := gnmi.NewServer(model, config, callback) - if err != nil { - return nil, err - } - return &server{Server: s}, nil -} - -// Get overrides the Get func of gnmi.Target to provide user auth. -func (s *server) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { - return s.Server.Get(ctx, req) -} - -// Set overrides the Set func of gnmi.Target to provide user auth. -/* -func (s *server) Set(ctx context.Context, req *pb.SetRequest) (*pb.SetResponse, error) { - msg, ok := credentials.AuthorizeUser(ctx) - if !ok { - log.Infof("denied a Set request: %v", msg) - return nil, status.Error(codes.PermissionDenied, msg) - } - log.Infof("allowed a Set request: %v", msg) - return s.Server.Set(ctx, req) -} -*/ - -// Target starts a gNMI target listening on the specified port. -func Target(bindAddr string) error { - entries := make([]*yang.Entry, 0) - for _, e := range oc.SchemaTree { - entries = append(entries, e) - } - - modelData, err := util.FindModelData(entries) - if err != nil { - return err - } - // Google stuff from here - model := gnmi.NewModel( - modelData, - reflect.TypeOf((*oc.Device)(nil)), - oc.SchemaTree["Device"], - oc.Unmarshal, - oc.ΛEnum) - - g := grpc.NewServer() - - var configData []byte - s, err := newServer(model, configData) - if err != nil { - return err - } - pb.RegisterGNMIServer(g, s) - reflection.Register(g) - - log.Infof("starting to listen on %s", bindAddr) - listen, err := net.Listen("tcp", bindAddr) - if err != nil { - return err - } - - log.Info("starting to serve") - if err := g.Serve(listen); err != nil { - return err - } - return nil -} 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 becc83abf853f78228e31eead79b75064956815a..37c76a364c89c6993d22708998054017f7f0322c 100644 --- a/build/ci/.deploy-k8s.yml +++ b/build/ci/.deploy-k8s.yml @@ -17,7 +17,6 @@ build:k8s-bot: image: name: bitnami/kubectl:latest entrypoint: [""] - before_script: - echo "override global before script" variables: diff --git a/build/ci/.golangci-config/.golangci.yml b/build/ci/.golangci-config/.golangci.yml index 3492086f612c3f9ff49695cd0164f995a3051062..1a2b9183717c0c1a95b8782e15a5189ee2059c10 100644 --- a/build/ci/.golangci-config/.golangci.yml +++ b/build/ci/.golangci-config/.golangci.yml @@ -27,7 +27,7 @@ linters: enable: - gofmt - goimports - - golint + - revive - gocyclo - govet issues: diff --git a/build/ci/.test.yml b/build/ci/.test.yml index 461480c311a45cb4a8721f501cd37c7910078dfc..4af5c562e81006edf8758d781a69e9cdba840773 100644 --- a/build/ci/.test.yml +++ b/build/ci/.test.yml @@ -1,12 +1,13 @@ -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 + GOSDN_TEST_API_ENDPOINT: gosdn-$CI_COMMIT_SHA.apps.ocp.fbi.h-da.de GOSDN_TEST_ENDPOINT_IP: 141.100.70.171 GOSDN_CHANGE_TIMEOUT: "100ms" rules: @@ -20,10 +21,38 @@ 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: + - cd ./test/integration + - ./../../build/cd/k8s-bot + - go test -race -v -run TestCmdIntegration + +integration-test:cli: + <<: *integration-test script: - ./build/ci/generate_port.sh $CI_PIPELINE_ID >> port - export GOSDN_TEST_ENDPOINT_PORT=$(cat port) - - go test -race ./test/integration -v -coverprofile=coverage.out + - cd ./api + - go test -race -v -run TestApiIntegration .test: &test image: golang:1.16 @@ -49,4 +78,4 @@ unit-test: controller-test: script: - go test -race -v -run TestRun - <<: *test \ No newline at end of file + <<: *test diff --git a/controller.go b/controller.go index f286874790320c61744951965ac6891a329105e4..f7f90f4668d26dbf9f7cfee92f58775b9c8ac592 100644 --- a/controller.go +++ b/controller.go @@ -71,6 +71,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 f00c205ddc15db40bc315bd1d1935a69c6e32b61..60e1819514a94ca9fc97f316e0e7dcff6ad31380 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.16 require ( code.fbi.h-da.de/cocsn/api/go v0.0.0-20210609143151-4dabee5ab99a - code.fbi.h-da.de/cocsn/gosdn/api v0.0.0-20210610093733-cbc69d7e537d code.fbi.h-da.de/cocsn/yang-models v0.0.7 github.com/aristanetworks/goarista v0.0.0-20201120222254-94a892eb0c6a github.com/docker/docker v20.10.6+incompatible diff --git a/go.sum b/go.sum index 74dfc0175dd4ab8ef2afc908b4936cd85b46f5e1..22b65647e51ec81fcc37ff1f8df5a26f412b6d70 100644 --- a/go.sum +++ b/go.sum @@ -21,12 +21,8 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -code.fbi.h-da.de/cocsn/api/go v0.0.0-20210609120033-1ef56612bd26/go.mod h1:2+rnE92IyXLbiy3/92EM7JrtsY5tXPAKX90QmsT2+m0= code.fbi.h-da.de/cocsn/api/go v0.0.0-20210609143151-4dabee5ab99a h1:C2tdh2A4RckZJXUymtF2ec8hE8mTkhn7EQJShv+i/Jk= code.fbi.h-da.de/cocsn/api/go v0.0.0-20210609143151-4dabee5ab99a/go.mod h1:2+rnE92IyXLbiy3/92EM7JrtsY5tXPAKX90QmsT2+m0= -code.fbi.h-da.de/cocsn/gosdn v0.0.3-0.20210609130706-9cca50b3d195/go.mod h1:EK0GUnwNB+qMMjSJxGzLpfDw+KsUHS2LZkdzshldm4Q= -code.fbi.h-da.de/cocsn/gosdn/api v0.0.0-20210610093733-cbc69d7e537d h1:olcB+ojVhToetF73X1waLRaTG7oZpdFvZ2yUIAKiiI8= -code.fbi.h-da.de/cocsn/gosdn/api v0.0.0-20210610093733-cbc69d7e537d/go.mod h1:rn/FEXBauGQyFOIS2NhGd0EzF29Ibds0xt34Oo5wTgk= code.fbi.h-da.de/cocsn/yang-models v0.0.7 h1:3TOo8J+EdAJKeq4o3aaNWZRhjSwguIS8wciW1U9PkSk= code.fbi.h-da.de/cocsn/yang-models v0.0.7/go.mod h1:M+2HinfhTT8nA8qvn2cpWNlOtuiizTNDWA3yfy72K/g= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -237,11 +233,11 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= @@ -317,22 +313,18 @@ github.com/neo4j/neo4j-go-driver v1.8.3/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcL github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc= github.com/openconfig/gnmi v0.0.0-20200414194230-1597cc0f2600/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A= github.com/openconfig/gnmi v0.0.0-20200508230933-d19cebf5e7be/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A= @@ -533,7 +525,6 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -570,7 +561,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -725,6 +715,7 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= diff --git a/http_test.go b/http_test.go index 1d1d311bce7969e2c6b18dfd0fbab48deca3b148..7b4926e16be0ee4548124b825d02e1cb8fcdcc99 100644 --- a/http_test.go +++ b/http_test.go @@ -6,9 +6,7 @@ import ( ) func Test_httpApi(t *testing.T) { - if testing.Short() { - t.Skip("this test is executed separately") - } + tests := []struct { name string request string diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go index 1185107479694aae1b22f77ee9e2d9ea26035169..77f078aa69c32a501adde00c46d2d1fc3f5a1677 100644 --- a/nucleus/gnmi_transport.go +++ b/nucleus/gnmi_transport.go @@ -41,24 +41,29 @@ type Gnmi struct { config *gnmi.Config } -// NewGnmiTransport takes a struct of GnmiTransportOptions and returns a Gnmi +// newGnmiTransport takes a struct of GnmiTransportOptions and returns a Gnmi // transport based on the values of it. -func NewGnmiTransport(opts *tpb.TransportOption, sbi southbound.SouthboundInterface) (*Gnmi, error) { +// Do not call directly. Use NewTransport() instead. +func newGnmiTransport(opts *tpb.TransportOption, sbi southbound.SouthboundInterface) (*Gnmi, error) { + if opts == nil || sbi == nil { + return nil, &errors.ErrInvalidParameters{} + } else if opts.TransportOption == nil { + return nil, &errors.ErrInvalidParameters{} + } gnmiConfig := &gnmi.Config{ Addr: opts.Address, Password: opts.Password, Username: opts.Username, TLS: opts.Tls, - Compression: opts.GetGnmiTransportOption().Compression, + Compression: opts.GetGnmiTransportOption().GetCompression(), } 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(), @@ -230,10 +235,7 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch val, ok := update.Val.Value.(*gpb.TypedValue_JsonIetfVal) if ok { opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}} - if err := g.Unmarshal(val.JsonIetfVal, pathutils.ToStrings(fullPath), root, opts...); err != nil { - return err - } - return nil + return g.Unmarshal(val.JsonIetfVal, pathutils.ToStrings(fullPath), root, opts...) } // TODO(mk): Evaluate hardcoded model key schema := models["Device"] diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go index 597cecd8ff954aa2b623df9f339115014ce08266..e3d418e741e8d25ff903b15ee856131b82db7a44 100644 --- a/nucleus/gnmi_transport_test.go +++ b/nucleus/gnmi_transport_test.go @@ -431,8 +431,6 @@ func TestGnmi_getWithRequest(t *testing.T) { } func TestNewGnmiTransport(t *testing.T) { - // TODO: revise test - t.Skip("deep equal for gnmi.Config broken") type args struct { opts *tpb.TransportOption } @@ -442,35 +440,6 @@ func TestNewGnmiTransport(t *testing.T) { want *Gnmi wantErr bool }{ - { - name: "default", - args: args{ - opts: &tpb.TransportOption{ - Address: "localhost:13371", - Username: "test", - Password: "test", - TransportOption: &tpb.TransportOption_GnmiTransportOption{ - GnmiTransportOption: &tpb.GnmiTransportOption{}, - }, - }}, - want: &Gnmi{ - Options: &tpb.TransportOption{ - Address: "localhost:13371", - Username: "test", - Password: "test", - TransportOption: &tpb.TransportOption_GnmiTransportOption{ - GnmiTransportOption: &tpb.GnmiTransportOption{}, - }, - }, - client: nil, - config: &gnmi.Config{ - Addr: "localhost:13371", - Password: "test", - Username: "test", - }, - }, - wantErr: false, - }, { name: "unsupported compression", args: args{ @@ -483,34 +452,6 @@ func TestNewGnmiTransport(t *testing.T) { want: nil, wantErr: true, }, - { - name: "certificate error no key file", - args: args{ - opts: &tpb.TransportOption{ - TransportOption: &tpb.TransportOption_GnmiTransportOption{ - GnmiTransportOption: &tpb.GnmiTransportOption{ - GrpcDialOptions: map[string]string{ - "cert-file": "invalid", - }, - }, - }}}, - want: nil, - wantErr: true, - }, - { - name: "certificate error no ca file", - args: args{ - opts: &tpb.TransportOption{ - TransportOption: &tpb.TransportOption_GnmiTransportOption{ - GnmiTransportOption: &tpb.GnmiTransportOption{ - GrpcDialOptions: map[string]string{ - "ca-file": "invalid", - }, - }, - }}}, - want: nil, - wantErr: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -518,7 +459,7 @@ func TestNewGnmiTransport(t *testing.T) { startGnmiTarget <- gnmiConfig.Addr } - got, err := NewGnmiTransport(tt.args.opts, NewSBI(spb.Type_OPENCONFIG)) + got, err := newGnmiTransport(tt.args.opts, NewSBI(spb.Type_OPENCONFIG)) if (err != nil) != tt.wantErr { t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr) return @@ -530,7 +471,7 @@ func TestNewGnmiTransport(t *testing.T) { tt.want.RespChan = got.RespChan } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewGnmiTransport() got = %v, want %v", got, tt.want) + t.Errorf("NewGnmiTransport() got = %#v, want %#v", got, tt.want) } if tt.name == "default" { stopGnmiTarget <- true diff --git a/nucleus/southbound.go b/nucleus/southbound.go index 0f2eda136b167053125b66d67890f6657b39699c..8538d7537e7fba8a72cab2f0a83e011ec455a3d5 100644 --- a/nucleus/southbound.go +++ b/nucleus/southbound.go @@ -57,10 +57,7 @@ func (oc *OpenConfig) Schema() *ytypes.Schema { // Needed for type assertion. func (oc *OpenConfig) SetNode() func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error { return func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error { - if err := ytypes.SetNode(schema, root.(*openconfig.Device), path, val, opts...); err != nil { - return err - } - return nil + return ytypes.SetNode(schema, root.(*openconfig.Device), path, val, opts...) } } diff --git a/nucleus/store.go b/nucleus/store.go index 94a741bc69e395b833e0267fecd3452774a49b6b..1a20b5833ba4202e3e996b5bfffdc6435e14678c 100644 --- a/nucleus/store.go +++ b/nucleus/store.go @@ -68,7 +68,9 @@ func (s genericStore) Add(item store.Storable) error { } // Get takes a Storable's UUID and returns the Storable. If the requested -// Storable does not exist an error is returned. +// Storable does not exist an error is returned. Get is only type safe for +// this Storable interface. For type safe get operations on specialised stores +// use GetDevice, GetPND, GetSBI, or GetChange respectively. func (s genericStore) Get(id uuid.UUID) (store.Storable, error) { if !s.Exists(id) { return nil, &errors.ErrNotFound{ID: id} diff --git a/nucleus/store_test.go b/nucleus/store_test.go index abba8efcc2153e642bca10c222ea629aa3834eb3..8468a34d458e438b390bb25ce6a57602e32374b7 100644 --- a/nucleus/store_test.go +++ b/nucleus/store_test.go @@ -272,18 +272,6 @@ func Test_sbiStore_get(t *testing.T) { args: args{id: defaultSbiID}, wantErr: true, }, - { - name: "fails wrong type", - fields: fields{ - genericStore: genericStore{ - did: &CommonDevice{ - UUID: did, - }, - }, - }, - args: args{id: did}, - wantErr: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -345,18 +333,6 @@ func Test_pndStore_get(t *testing.T) { args: args{id: defaultPndID}, wantErr: true, }, - { - name: "fails wrong type", - fields: fields{ - genericStore: genericStore{ - did: &CommonDevice{ - UUID: did, - }, - }, - }, - args: args{id: did}, - wantErr: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/nucleus/transport.go b/nucleus/transport.go index 3d8523e954d448a9fa461b42bacf56bf8b83e668..f946dc332ddca903417f6f922635547e997074a1 100644 --- a/nucleus/transport.go +++ b/nucleus/transport.go @@ -16,11 +16,24 @@ func NewTransport(opts *tpb.TransportOption, sbi southbound.SouthboundInterface) Param: "'opt' cannot be 'nil'", } } + if sbi == nil { + return nil, errors.ErrInvalidParameters{ + Param: "'sbi' cannot be 'nil'", + } + } + if !validTransportOptions(opts) { + return nil, &errors.ErrInvalidTransportOptions{Opt: opts} + } switch o := opts.TransportOption.(type) { case *tpb.TransportOption_GnmiTransportOption: - return NewGnmiTransport(opts, sbi) + return newGnmiTransport(opts, sbi) default: return nil, &errors.ErrInvalidTransportOptions{Opt: o} } } + +func validTransportOptions(opt *tpb.TransportOption) bool { + return opt.GetGnmiTransportOption() != nil || + opt.GetRestconfTransportOption() != nil +} diff --git a/nucleus/transport_test.go b/nucleus/transport_test.go index 787c12a36021b1efe304a388b49092dd8515910d..5a6264c261065d043531897a6896eb76e6a18ab8 100644 --- a/nucleus/transport_test.go +++ b/nucleus/transport_test.go @@ -1,14 +1,15 @@ package nucleus import ( - "reflect" "testing" tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport" "code.fbi.h-da.de/cocsn/gosdn/interfaces/southbound" - "code.fbi.h-da.de/cocsn/gosdn/interfaces/transport" + gpb "github.com/openconfig/gnmi/proto/gnmi" ) +// TestNewTransport is for input validation only. Functional tests +// are conducted at the transport implementation constructors. func TestNewTransport(t *testing.T) { type args struct { opts *tpb.TransportOption @@ -17,21 +18,91 @@ func TestNewTransport(t *testing.T) { tests := []struct { name string args args - want transport.Transport wantErr bool }{ - // TODO: Add test cases. + { + name: "default", + args: args{ + opts: &tpb.TransportOption{ + Address: "test", + Username: "test", + Password: "test", + Tls: false, + Csbi: false, + TransportOption: &tpb.TransportOption_GnmiTransportOption{ + GnmiTransportOption: &tpb.GnmiTransportOption{ + Encoding: gpb.Encoding_PROTO, + }, + }, + }, + sbi: &OpenConfig{}, + }, + wantErr: false, + }, + { + name: "no opt", + args: args{ + opts: nil, + sbi: &OpenConfig{}, + }, + wantErr: true, + }, + { + name: "no sbi", + args: args{ + opts: &tpb.TransportOption{ + Address: "test", + Username: "test", + Password: "test", + Tls: false, + Csbi: false, + TransportOption: &tpb.TransportOption_GnmiTransportOption{ + GnmiTransportOption: &tpb.GnmiTransportOption{ + Encoding: gpb.Encoding_PROTO, + }, + }, + }, + sbi: nil, + }, + wantErr: true, + }, + { + name: "no implementation options", + args: args{ + opts: &tpb.TransportOption{ + Address: "test", + Username: "test", + Password: "test", + Tls: false, + Csbi: false, + }, + sbi: &OpenConfig{}, + }, + wantErr: true, + }, + { + name: "no inner implementation options", + args: args{ + opts: &tpb.TransportOption{ + Address: "test", + Username: "test", + Password: "test", + Tls: false, + Csbi: false, + TransportOption: &tpb.TransportOption_GnmiTransportOption{}, + }, + sbi: &OpenConfig{}, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewTransport(tt.args.opts, tt.args.sbi) + _, err := NewTransport(tt.args.opts, tt.args.sbi) if (err != nil) != tt.wantErr { t.Errorf("NewTransport() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewTransport() got = %v, want %v", got, tt.want) - } }) } } diff --git a/test/integration/cliIntegration_test.go b/test/integration/cliIntegration_test.go deleted file mode 100644 index 97eda04e8cb0175fd76d3301bb42d5ec91bf8c73..0000000000000000000000000000000000000000 --- a/test/integration/cliIntegration_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package integration - -import ( - "testing" - - "code.fbi.h-da.de/cocsn/gosdn/api" -) - -func TestCapabilities(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test") - } - type args struct { - a string - u string - p string - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "default", - args: args{ - a: testAddress, - u: testUsername, - p: testPassword, - }, - wantErr: false, - }, - { - name: "destination unreachable", - args: args{ - a: unreachable, - u: testUsername, - p: testPassword, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := api.Capabilities(tt.args.a, tt.args.u, tt.args.p); (err != nil) != tt.wantErr { - t.Errorf("Capabilities() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/test/integration/nucleusIntegration_test.go b/test/integration/nucleusIntegration_test.go index 4a5a29eebeb8d7acb0c0fdf7310314b83000694a..f92e93168ab2b35c4c16360d36e4fc1a3ea5593a 100644 --- a/test/integration/nucleusIntegration_test.go +++ b/test/integration/nucleusIntegration_test.go @@ -2,6 +2,8 @@ package integration import ( "context" + "fmt" + "os" "reflect" "sort" "testing" @@ -12,13 +14,95 @@ import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" "code.fbi.h-da.de/cocsn/gosdn/nucleus" + "code.fbi.h-da.de/cocsn/gosdn/nucleus/errors" "code.fbi.h-da.de/cocsn/gosdn/nucleus/types" + "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto" gpb "github.com/openconfig/gnmi/proto/gnmi" + log "github.com/sirupsen/logrus" pb "google.golang.org/protobuf/proto" ) +const unreachable = "203.0.113.10:6030" +const testPath = "/system/config/hostname" + +var testIP = "141.100.70.171" +var testPort = "6030" +var testAddress = testIP + ":" + testPort +var testAPIEndpoint = "gosdn-latest.apps.ocp.fbi.h-da.de" +var testUsername = "admin" +var testPassword = "arista" +var opt *tpb.TransportOption var gnmiMessages map[string]pb.Message +func TestMain(m *testing.M) { + testSetupIntegration() + os.Exit(m.Run()) +} + +func testSetupIntegration() { + if os.Getenv("GOSDN_LOG") == "nolog" { + log.SetLevel(log.PanicLevel) + } + + a := os.Getenv("GOSDN_TEST_ENDPOINT_IP") + if a != "" { + testIP = a + log.Infof("GOSDN_TEST_ENDPOINT_IP set to %v", testIP) + } + port := os.Getenv("GOSDN_TEST_ENDPOINT_PORT") + if port != "" { + testPort = port + log.Infof("GOSDN_TEST_ENDPOINT_PORT set to %v", testPort) + } + testAddress = testIP + ":" + testPort + fmt.Printf("Testadress: %s, Testport: %s, Port: %s", testAddress, testPort, port) + + api := os.Getenv("GOSDN_TEST_API_ENDPOINT") + if api != "" { + testAPIEndpoint = api + log.Infof("GOSDN_TEST_API_ENDPOINT set to %v", testAPIEndpoint) + } + u := os.Getenv("GOSDN_TEST_USER") + if u != "" { + testUsername = u + log.Infof("GOSDN_TEST_USER set to %v", testUsername) + } + p := os.Getenv("GOSDN_TEST_PASSWORD") + if p != "" { + testPassword = p + log.Infof("GOSDN_TEST_PASSWORD set to %v", testPassword) + } + + gnmiMessages = map[string]pb.Message{ + "../proto/cap-resp-arista-ceos": &gpb.CapabilityResponse{}, + "../proto/req-full-node": &gpb.GetRequest{}, + "../proto/req-full-node-arista-ceos": &gpb.GetRequest{}, + "../proto/req-interfaces-arista-ceos": &gpb.GetRequest{}, + "../proto/req-interfaces-interface-arista-ceos": &gpb.GetRequest{}, + "../proto/req-interfaces-wildcard": &gpb.GetRequest{}, + "../proto/resp-full-node": &gpb.GetResponse{}, + "../proto/resp-full-node-arista-ceos": &gpb.GetResponse{}, + "../proto/resp-interfaces-arista-ceos": &gpb.GetResponse{}, + "../proto/resp-interfaces-interface-arista-ceos": &gpb.GetResponse{}, + "../proto/resp-interfaces-wildcard": &gpb.GetResponse{}, + "../proto/resp-set-system-config-hostname": &gpb.SetResponse{}, + } + for k, v := range gnmiMessages { + if err := proto.Read(k, v); err != nil { + log.Fatalf("error parsing %v: %v", k, err) + } + } + + opt = &tpb.TransportOption{ + Address: testAddress, + Username: testUsername, + Password: testPassword, + TransportOption: &tpb.TransportOption_GnmiTransportOption{ + GnmiTransportOption: &tpb.GnmiTransportOption{}, + }, + } +} + func TestGnmi_SetIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") @@ -41,6 +125,8 @@ func TestGnmi_SetIntegration(t *testing.T) { fields: fields{ opt: &tpb.TransportOption{ Address: "203.0.113.10:6030", + TransportOption: &tpb.TransportOption_GnmiTransportOption{ + GnmiTransportOption: &tpb.GnmiTransportOption{}}, }, }, args: args{ @@ -70,7 +156,7 @@ func TestGnmi_SetIntegration(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - g, err := nucleus.NewGnmiTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG)) + g, err := nucleus.NewTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG)) if err != nil { t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr) return @@ -134,7 +220,7 @@ func TestGnmi_GetIntegration(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - g, err := nucleus.NewGnmiTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG)) + g, err := nucleus.NewTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG)) if err != nil { t.Error(err) return @@ -172,9 +258,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{ @@ -238,7 +324,7 @@ func TestGnmi_SubscribeIntegration(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var wantErr = tt.wantErr - g, err := nucleus.NewGnmiTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG)) + g, err := nucleus.NewTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG)) if err != nil { t.Error(err) return @@ -250,7 +336,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) } } } @@ -313,11 +399,15 @@ func TestGnmi_CapabilitiesIntegration(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - g, err := nucleus.NewGnmiTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG)) + tr, err := nucleus.NewTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG)) if err != nil { t.Error(err) return } + g, ok := tr.(*nucleus.Gnmi) + if !ok { + t.Error(&errors.ErrInvalidTypeAssertion{}) + } resp, err := g.Capabilities(tt.args.ctx) if (err != nil) != tt.wantErr { t.Errorf("Capabilities() error = %v, wantErr %v", err, tt.wantErr)