Restructure code and create module library 03/101003/5
authorRitu Sood <ritu.sood@intel.com>
Sat, 1 Feb 2020 07:29:51 +0000 (23:29 -0800)
committerSrivahni Chivukula <srivahni.chivukula@intel.com>
Fri, 14 Feb 2020 11:54:11 +0000 (03:54 -0800)
Restructures and moves code to make it
aligned with the current design.
https://wiki.onap.org/display/DW/Multi+Cluster+Application+Scheduler
examples/example_module.go shows how to
import and use modules from this package.
Patch#2 Updated example

Issue-ID: MULTICLOUD-871
Signed-off-by: Ritu Sood <ritu.sood@intel.com>
Change-Id: Ia1e9802a946a07dcca8f79f0e2250933ab3efa66

20 files changed:
src/orchestrator/api/api.go
src/orchestrator/api/projecthandler.go
src/orchestrator/api/projecthandler_test.go
src/orchestrator/cmd/main.go
src/orchestrator/examples/example_module.go [new file with mode: 0644]
src/orchestrator/go.sum
src/orchestrator/pkg/infra/auth/auth.go [moved from src/orchestrator/internal/auth/auth.go with 100% similarity]
src/orchestrator/pkg/infra/auth/auth_test.go [moved from src/orchestrator/internal/auth/auth_test.go with 88% similarity]
src/orchestrator/pkg/infra/config/config.go [moved from src/orchestrator/internal/config/config.go with 96% similarity]
src/orchestrator/pkg/infra/config/config_test.go [moved from src/orchestrator/internal/config/config_test.go with 94% similarity]
src/orchestrator/pkg/infra/db/README.md [moved from src/orchestrator/internal/db/README.md with 100% similarity]
src/orchestrator/pkg/infra/db/mock.go [moved from src/orchestrator/internal/db/mock.go with 100% similarity]
src/orchestrator/pkg/infra/db/mongo.go [moved from src/orchestrator/internal/db/mongo.go with 99% similarity]
src/orchestrator/pkg/infra/db/mongo_test.go [moved from src/orchestrator/internal/db/mongo_test.go with 100% similarity]
src/orchestrator/pkg/infra/db/store.go [moved from src/orchestrator/internal/db/store.go with 97% similarity]
src/orchestrator/pkg/infra/db/store_test.go [moved from src/orchestrator/internal/db/store_test.go with 100% similarity]
src/orchestrator/pkg/infra/logutils/logger.go [moved from src/orchestrator/internal/logutils/logger.go with 100% similarity]
src/orchestrator/pkg/module/module.go [new file with mode: 0644]
src/orchestrator/pkg/module/project.go [moved from src/orchestrator/internal/project/project.go with 79% similarity]
src/orchestrator/pkg/module/project_test.go [moved from src/orchestrator/internal/project/project_test.go with 95% similarity]

index 83f17bb..e37b158 100644 (file)
@@ -10,29 +10,28 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
-
 package api
 
 import (
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/project"
+       moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
 
        "github.com/gorilla/mux"
 )
-
+var moduleClient *moduleLib.Client
 // NewRouter creates a router that registers the various urls that are supported
-func NewRouter(projectClient project.ProjectManager) *mux.Router {
+func NewRouter(projectClient moduleLib.ProjectManager) *mux.Router {
 
        router := mux.NewRouter().PathPrefix("/v2").Subrouter()
-
+       moduleClient = moduleLib.NewClient()
        if projectClient == nil {
-               projectClient = project.NewProjectClient()
+               projectClient = moduleClient.Project
        }
        projHandler := projectHandler{
                client: projectClient,
        }
-       router.HandleFunc("/project", projHandler.createHandler).Methods("POST")
-       router.HandleFunc("/project/{project-name}", projHandler.getHandler).Methods("GET")
-       router.HandleFunc("/project/{project-name}", projHandler.deleteHandler).Methods("DELETE")
+       router.HandleFunc("/projects", projHandler.createHandler).Methods("POST")
+       router.HandleFunc("/projects/{project-name}", projHandler.getHandler).Methods("GET")
+       router.HandleFunc("/projects/{project-name}", projHandler.deleteHandler).Methods("DELETE")
 
        return router
-}
+}
\ No newline at end of file
index 30f21de..1830b91 100644 (file)
@@ -21,7 +21,7 @@ import (
        "io"
        "net/http"
 
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/project"
+       moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
 
        "github.com/gorilla/mux"
 )
@@ -31,12 +31,12 @@ import (
 type projectHandler struct {
        // Interface that implements Project operations
        // We will set this variable with a mock interface for testing
-       client project.ProjectManager
+       client moduleLib.ProjectManager
 }
 
 // Create handles creation of the Project entry in the database
 func (h projectHandler) createHandler(w http.ResponseWriter, r *http.Request) {
-       var p project.Project
+       var p moduleLib.Project
 
        err := json.NewDecoder(r.Body).Decode(&p)
        switch {
@@ -54,7 +54,7 @@ func (h projectHandler) createHandler(w http.ResponseWriter, r *http.Request) {
                return
        }
 
-       ret, err := h.client.Create(p)
+       ret, err := h.client.CreateProject(p)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
@@ -75,7 +75,7 @@ func (h projectHandler) getHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        name := vars["project-name"]
 
-       ret, err := h.client.Get(name)
+       ret, err := h.client.GetProject(name)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
@@ -95,7 +95,7 @@ func (h projectHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        name := vars["project-name"]
 
-       err := h.client.Delete(name)
+       err := h.client.DeleteProject(name)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
index 2699f2e..41f515d 100644 (file)
@@ -25,7 +25,7 @@ import (
        "reflect"
        "testing"
 
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/project"
+       moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
 
        pkgerrors "github.com/pkg/errors"
 )
@@ -36,27 +36,27 @@ import (
 type mockProjectManager struct {
        // Items and err will be used to customize each test
        // via a localized instantiation of mockProjectManager
-       Items []project.Project
+       Items []moduleLib.Project
        Err   error
 }
 
-func (m *mockProjectManager) Create(inp project.Project) (project.Project, error) {
+func (m *mockProjectManager) CreateProject(inp moduleLib.Project) (moduleLib.Project, error) {
        if m.Err != nil {
-               return project.Project{}, m.Err
+               return moduleLib.Project{}, m.Err
        }
 
        return m.Items[0], nil
 }
 
-func (m *mockProjectManager) Get(name string) (project.Project, error) {
+func (m *mockProjectManager) GetProject(name string) (moduleLib.Project, error) {
        if m.Err != nil {
-               return project.Project{}, m.Err
+               return moduleLib.Project{}, m.Err
        }
 
        return m.Items[0], nil
 }
 
-func (m *mockProjectManager) Delete(name string) error {
+func (m *mockProjectManager) DeleteProject(name string) error {
        return m.Err
 }
 
@@ -64,7 +64,7 @@ func TestProjectCreateHandler(t *testing.T) {
        testCases := []struct {
                label         string
                reader        io.Reader
-               expected      project.Project
+               expected      moduleLib.Project
                expectedCode  int
                projectClient *mockProjectManager
        }{
@@ -80,13 +80,13 @@ func TestProjectCreateHandler(t *testing.T) {
                                "project-name":"testProject",
                                "description":"Test Project used for unit testing"
                                }`)),
-                       expected: project.Project{
+                       expected: moduleLib.Project{
                                ProjectName: "testProject",
                                Description: "Test Project used for unit testing",
                        },
                        projectClient: &mockProjectManager{
                                //Items that will be returned by the mocked Client
-                               Items: []project.Project{
+                               Items: []moduleLib.Project{
                                        {
                                                ProjectName: "testProject",
                                                Description: "Test Project used for unit testing",
@@ -106,7 +106,7 @@ func TestProjectCreateHandler(t *testing.T) {
 
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
-                       request := httptest.NewRequest("POST", "/v2/project", testCase.reader)
+                       request := httptest.NewRequest("POST", "/v2/projects", testCase.reader)
                        resp := executeRequest(request, NewRouter(testCase.projectClient))
 
                        //Check returned code
@@ -116,7 +116,7 @@ func TestProjectCreateHandler(t *testing.T) {
 
                        //Check returned body only if statusCreated
                        if resp.StatusCode == http.StatusCreated {
-                               got := project.Project{}
+                               got := moduleLib.Project{}
                                json.NewDecoder(resp.Body).Decode(&got)
 
                                if reflect.DeepEqual(testCase.expected, got) == false {
@@ -132,7 +132,7 @@ func TestProjectGetHandler(t *testing.T) {
 
        testCases := []struct {
                label         string
-               expected      project.Project
+               expected      moduleLib.Project
                name, version string
                expectedCode  int
                projectClient *mockProjectManager
@@ -140,13 +140,13 @@ func TestProjectGetHandler(t *testing.T) {
                {
                        label:        "Get Project",
                        expectedCode: http.StatusOK,
-                       expected: project.Project{
+                       expected: moduleLib.Project{
                                ProjectName: "testProject",
                                Description: "A Test project for unit testing",
                        },
                        name: "testProject",
                        projectClient: &mockProjectManager{
-                               Items: []project.Project{
+                               Items: []moduleLib.Project{
                                        {
                                                ProjectName: "testProject",
                                                Description: "A Test project for unit testing",
@@ -159,7 +159,7 @@ func TestProjectGetHandler(t *testing.T) {
                        expectedCode: http.StatusInternalServerError,
                        name:         "nonexistingproject",
                        projectClient: &mockProjectManager{
-                               Items: []project.Project{},
+                               Items: []moduleLib.Project{},
                                Err:   pkgerrors.New("Internal Error"),
                        },
                },
@@ -167,7 +167,7 @@ func TestProjectGetHandler(t *testing.T) {
 
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
-                       request := httptest.NewRequest("GET", "/v2/project/"+testCase.name, nil)
+                       request := httptest.NewRequest("GET", "/v2/projects/"+testCase.name, nil)
                        resp := executeRequest(request, NewRouter(testCase.projectClient))
 
                        //Check returned code
@@ -177,7 +177,7 @@ func TestProjectGetHandler(t *testing.T) {
 
                        //Check returned body only if statusOK
                        if resp.StatusCode == http.StatusOK {
-                               got := project.Project{}
+                               got := moduleLib.Project{}
                                json.NewDecoder(resp.Body).Decode(&got)
 
                                if reflect.DeepEqual(testCase.expected, got) == false {
@@ -216,7 +216,7 @@ func TestProjectDeleteHandler(t *testing.T) {
 
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
-                       request := httptest.NewRequest("DELETE", "/v2/project/"+testCase.name, nil)
+                       request := httptest.NewRequest("DELETE", "/v2/projects/"+testCase.name, nil)
                        resp := executeRequest(request, NewRouter(testCase.projectClient))
 
                        //Check returned code
index 657d5bf..fb8f26d 100644 (file)
@@ -23,9 +23,9 @@ import (
        "time"
 
        "github.com/onap/multicloud-k8s/src/orchestrator/api"
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/auth"
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/config"
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/db"
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/auth"
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config"
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
 
        "github.com/gorilla/handlers"
 )
diff --git a/src/orchestrator/examples/example_module.go b/src/orchestrator/examples/example_module.go
new file mode 100644 (file)
index 0000000..29ecdc2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package test
+
+import (
+       moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
+       "log"
+)
+
+// ExampleClient_Project to test Project
+func ExampleClient_Project() {
+       // Get handle to the client
+       c := moduleLib.NewClient()
+       // Check if project is initialized
+       if c.Project == nil {
+               log.Println("Project is Uninitialized")
+               return
+       }
+       // Perform operations on Project Module
+       _, err := c.Project.CreateProject(moduleLib.Project{ProjectName: "test"})
+       if err != nil {
+               log.Println(err)
+               return
+       }
+       _, err = c.Project.GetProject("test")
+       if err != nil {
+               log.Println(err)
+               return
+       }
+       err = c.Project.DeleteProject("test")
+       if err != nil {
+               log.Println(err)
+       }
+}
index 732bc28..d201540 100644 (file)
@@ -171,6 +171,7 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/onap/multicloud-k8s v0.0.0-20191115005109-f168ebb73d8d h1:3uFucXVv6gqa3H1u85CjoLOvGraREfD8/NL7m/9W9tc=
+github.com/onap/multicloud-k8s v0.0.0-20200131010833-90e13d101cf0 h1:2qDo6s4pdg/g7Vj6QGrCK02EP4jjwVehgEObnAfipSM=
 github.com/onap/multicloud-k8s/src/k8splugin v0.0.0-20191115005109-f168ebb73d8d h1:ucIEjqzNVeFPnQofeuBfUqro0OnilX//fajEFxuLsgA=
 github.com/onap/multicloud-k8s/src/k8splugin v0.0.0-20191115005109-f168ebb73d8d/go.mod h1:EnQd/vQGZR1/55IihaHxiux4ZUig/zfXZux7bfmU0S8=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
similarity index 88%
rename from src/orchestrator/internal/auth/auth_test.go
rename to src/orchestrator/pkg/infra/auth/auth_test.go
index e41cb1a..fdf81e6 100644 (file)
@@ -28,9 +28,9 @@ func TestGetTLSConfig(t *testing.T) {
        if err == nil {
                t.Errorf("Test failed, expected error but got none")
        }
-       tlsConfig, err := GetTLSConfig("../../tests/certs/auth_test_certificate.pem",
-               "../../tests/certs/auth_test_certificate.pem",
-               "../../tests/certs/auth_test_key.pem")
+       tlsConfig, err := GetTLSConfig("../../../tests/certs/auth_test_certificate.pem",
+               "../../../tests/certs/auth_test_certificate.pem",
+               "../../../tests/certs/auth_test_key.pem")
        if err != nil {
                t.Fatal("Test Failed as GetTLSConfig returned error: " + err.Error())
        }
similarity index 96%
rename from src/orchestrator/internal/config/config.go
rename to src/orchestrator/pkg/infra/config/config.go
index cb4656f..df9cec9 100644 (file)
@@ -83,9 +83,9 @@ func defaultConfiguration() *Configuration {
                DatabaseType:        "mongo",
                PluginDir:           cwd,
                EtcdIP:              "127.0.0.1",
-               EtcdCert:            "etcd.cert",
-               EtcdKey:             "etcd.key",
-               EtcdCAFile:          "etcd-ca.cert",
+               EtcdCert:            "",
+               EtcdKey:             "",
+               EtcdCAFile:          "",
                ServicePort:         "9015",
                KubernetesLabelName: "orchestrator.io/rb-instance-id",
        }
@@ -29,7 +29,7 @@ func TestReadConfigurationFile(t *testing.T) {
        })
 
        t.Run("Read Configuration File", func(t *testing.T) {
-               conf, err := readConfigFile("../../tests/configs/mock_config.json")
+               conf, err := readConfigFile("../../../tests/configs/mock_config.json")
                if err != nil {
                        t.Fatal("ReadConfigurationFile: Error reading file: ", err)
                }
similarity index 99%
rename from src/orchestrator/internal/db/mongo.go
rename to src/orchestrator/pkg/infra/db/mongo.go
index 3720a4f..32d0b54 100644 (file)
@@ -21,7 +21,7 @@ import (
 
        "golang.org/x/net/context"
 
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/config"
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config"
 
        pkgerrors "github.com/pkg/errors"
        "go.mongodb.org/mongo-driver/bson"
similarity index 97%
rename from src/orchestrator/internal/db/store.go
rename to src/orchestrator/pkg/infra/db/store.go
index ed39420..1a9632e 100644 (file)
@@ -17,7 +17,7 @@ import (
        "encoding/json"
        "reflect"
 
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/config"
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config"
 
        pkgerrors "github.com/pkg/errors"
 )
diff --git a/src/orchestrator/pkg/module/module.go b/src/orchestrator/pkg/module/module.go
new file mode 100644 (file)
index 0000000..e448209
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package module
+
+import (
+       )
+
+// Client for using the services in the orchestrator
+type Client struct {
+    Project *ProjectClient
+    // Add Clients for API's here
+}
+
+// NewClient creates a new client for using the services
+func NewClient() *Client {
+    c:= &Client{}
+    c.Project = NewProjectClient()
+    // Add Client API handlers here
+    return c
+}
\ No newline at end of file
similarity index 79%
rename from src/orchestrator/internal/project/project.go
rename to src/orchestrator/pkg/module/project.go
index f0c5006..e44164f 100644 (file)
  * limitations under the License.
  */
 
-package project
+package module
 
 import (
        "encoding/json"
 
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/db"
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
 
        pkgerrors "github.com/pkg/errors"
 )
@@ -47,14 +47,14 @@ func (pk ProjectKey) String() string {
        return string(out)
 }
 
-// ProjectManager is an interface exposes the Project functionality
+// Manager is an interface exposes the Project functionality
 type ProjectManager interface {
-       Create(pr Project) (Project, error)
-       Get(name string) (Project, error)
-       Delete(name string) error
+       CreateProject(pr Project) (Project, error)
+       GetProject(name string) (Project, error)
+       DeleteProject(name string) error
 }
 
-// ProjectClient implements the ProjectManager
+// ProjectClient implements the Manager
 // It will also be used to maintain some localized state
 type ProjectClient struct {
        storeName           string
@@ -62,15 +62,15 @@ type ProjectClient struct {
 }
 
 // NewProjectClient returns an instance of the ProjectClient
-// which implements the ProjectManager
+// which implements the Manager
 func NewProjectClient() *ProjectClient {
        return &ProjectClient{
                tagMeta: "projectmetadata",
        }
 }
 
-// Create a new collection based on the project
-func (v *ProjectClient) Create(p Project) (Project, error) {
+// CreateProject a new collection based on the project
+func (v *ProjectClient) CreateProject(p Project) (Project, error) {
 
        //Construct the composite key to select the entry
        key := ProjectKey{
@@ -78,7 +78,7 @@ func (v *ProjectClient) Create(p Project) (Project, error) {
        }
 
        //Check if this Project already exists
-       _, err := v.Get(p.ProjectName)
+       _, err := v.GetProject(p.ProjectName)
        if err == nil {
                return Project{}, pkgerrors.New("Project already exists")
        }
@@ -91,8 +91,8 @@ func (v *ProjectClient) Create(p Project) (Project, error) {
        return p, nil
 }
 
-// Get returns the Project for corresponding name
-func (v *ProjectClient) Get(name string) (Project, error) {
+// GetProject returns the Project for corresponding name
+func (v *ProjectClient) GetProject(name string) (Project, error) {
 
        //Construct the composite key to select the entry
        key := ProjectKey{
@@ -116,8 +116,8 @@ func (v *ProjectClient) Get(name string) (Project, error) {
        return Project{}, pkgerrors.New("Error getting Project")
 }
 
-// Delete the  Project from database
-func (v *ProjectClient) Delete(name string) error {
+// DeleteProject the  Project from database
+func (v *ProjectClient) DeleteProject(name string) error {
 
        //Construct the composite key to select the entry
        key := ProjectKey{
  * limitations under the License.
  */
 
-package project
+package module
 
 import (
        "reflect"
        "strings"
        "testing"
 
-       "github.com/onap/multicloud-k8s/src/orchestrator/internal/db"
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
 
        pkgerrors "github.com/pkg/errors"
 )
@@ -60,7 +60,7 @@ func TestCreateProject(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        db.DBconn = testCase.mockdb
                        impl := NewProjectClient()
-                       got, err := impl.Create(testCase.inp)
+                       got, err := impl.CreateProject(testCase.inp)
                        if err != nil {
                                if testCase.expectedError == "" {
                                        t.Fatalf("Create returned an unexpected error %s", err)
@@ -119,7 +119,7 @@ func TestGetProject(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        db.DBconn = testCase.mockdb
                        impl := NewProjectClient()
-                       got, err := impl.Get(testCase.name)
+                       got, err := impl.GetProject(testCase.name)
                        if err != nil {
                                if testCase.expectedError == "" {
                                        t.Fatalf("Get returned an unexpected error: %s", err)
@@ -163,7 +163,7 @@ func TestDeleteProject(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        db.DBconn = testCase.mockdb
                        impl := NewProjectClient()
-                       err := impl.Delete(testCase.name)
+                       err := impl.DeleteProject(testCase.name)
                        if err != nil {
                                if testCase.expectedError == "" {
                                        t.Fatalf("Delete returned an unexpected error %s", err)