Repository: cortexlabs/cortex (language: go)

Issue:
Running `cortex cluster export` throws a panic error
### Version

0.41

### Description

Running `cortex cluster export` throws a panic error. As reported by _Dipen Chawla_ in https://cortex-dot-dev.slack.com/archives/C01KHQRUWNT/p1641561660007800.

### Stack traces

```text
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x30d9e1b]

goroutine 1 [running]:
github.com/cortexlabs/cortex/pkg/lib/exit.Panic(0x3c94620, 0xc000477d10, 0x0, 0x0, 0x0)
	/home/circleci/project/pkg/lib/exit/exit.go:60 +0x1b5
github.com/cortexlabs/cortex/pkg/lib/exit.RecoverAndExit(0x0, 0x0, 0x0)
	/home/circleci/project/pkg/lib/exit/exit.go:66 +0xa5
panic(0x345dba0, 0x5093590)
	/usr/local/go/src/runtime/panic.go:965 +0x1b9
github.com/cortexlabs/cortex/cli/cmd.glob..func5(0x50b1140, 0xc0003f9b80, 0x0, 0x4)
	/home/circleci/project/cli/cmd/cluster.go:767 +0x59b
github.com/spf13/cobra.(*Command).execute(0x50b1140, 0xc0003f9b40, 0x4, 0x4, 0x50b1140, 0xc0003f9b40)
	/home/circleci/go/pkg/mod/github.com/spf13/cobra@v1.2.0/command.go:863 +0x2c2
github.com/spf13/cobra.(*Command).ExecuteC(0x50b2b80, 0xc0007bff40, 0x1, 0x1)
	/home/circleci/go/pkg/mod/github.com/spf13/cobra@v1.2.0/command.go:977 +0x375
github.com/spf13/cobra.(*Command).Execute(...)
	/home/circleci/go/pkg/mod/github.com/spf13/cobra@v1.2.0/command.go:905
github.com/cortexlabs/cortex/cli/cmd.Execute()
	/home/circleci/project/cli/cmd/root.go:171 +0x305
main.main()
	/home/circleci/project/cli/main.go:24 +0x25
```



Discussion / hints:

Fix patch:
diff --git a/cli/cmd/cluster.go b/cli/cmd/cluster.go
index 3240c560de..9a328c5da5 100644
--- a/cli/cmd/cluster.go
+++ b/cli/cmd/cluster.go
@@ -764,12 +764,16 @@ var _clusterExportCmd = &cobra.Command{
 			exit.Error(err)
 		}
 
-		for _, apiResponse := range apisResponse {
-			specFilePath := filepath.Join(exportPath, apiResponse.Spec.Name+".yaml")
+		for _, api := range apisResponse {
+			apisWithSpec, err := cluster.GetAPI(operatorConfig, api.Metadata.Name)
+			if err != nil {
+				exit.Error(err)
+			}
 
-			fmt.Println(fmt.Sprintf("exporting %s to %s", apiResponse.Spec.Name, specFilePath))
+			specFilePath := filepath.Join(exportPath, api.Metadata.Name+".yaml")
+			fmt.Println(fmt.Sprintf("exporting %s to %s", api.Metadata.Name, specFilePath))
 
-			yamlBytes, err := yaml.Marshal(apiResponse.Spec.API.SubmittedAPISpec)
+			yamlBytes, err := yaml.Marshal(apisWithSpec[0].Spec.API.SubmittedAPISpec)
 			if err != nil {
 				exit.Error(err)
 			}
diff --git a/cli/cmd/describe.go b/cli/cmd/describe.go
index 767045c5a2..8b34f5c06d 100644
--- a/cli/cmd/describe.go
+++ b/cli/cmd/describe.go
@@ -108,6 +108,6 @@ func describeAPI(env cliconfig.Environment, apiName string) (string, error) {
 	case userconfig.AsyncAPIKind:
 		return asyncDescribeAPITable(apiRes, env)
 	default:
-		return "", errors.ErrorUnexpected(fmt.Sprintf("encountered unexpected kind %s for api %s", apiRes.Spec.Kind, apiRes.Spec.Name))
+		return "", errors.ErrorUnexpected(fmt.Sprintf("encountered unexpected kind %s for api %s", apiRes.Metadata.Kind, apiRes.Metadata.Name))
 	}
 }
diff --git a/cli/cmd/get.go b/cli/cmd/get.go
index 89c44b2ce7..b0114f2a5a 100644
--- a/cli/cmd/get.go
+++ b/cli/cmd/get.go
@@ -127,7 +127,7 @@ var _getCmd = &cobra.Command{
 				}
 
 				var jobTable string
-				if apisRes[0].Spec.Kind == userconfig.BatchAPIKind {
+				if apisRes[0].Metadata.Kind == userconfig.BatchAPIKind {
 					jobTable, err = getBatchJob(env, args[0], args[1])
 				} else {
 					jobTable, err = getTaskJob(env, args[0], args[1])
@@ -484,7 +484,7 @@ func getAPI(env cliconfig.Environment, apiName string) (string, error) {
 
 	apiRes := apisRes[0]
 
-	switch apiRes.Spec.Kind {
+	switch apiRes.Metadata.Kind {
 	case userconfig.RealtimeAPIKind:
 		return realtimeAPITable(apiRes, env)
 	case userconfig.AsyncAPIKind:
@@ -496,7 +496,7 @@ func getAPI(env cliconfig.Environment, apiName string) (string, error) {
 	case userconfig.TaskAPIKind:
 		return taskAPITable(apiRes), nil
 	default:
-		return "", errors.ErrorUnexpected(fmt.Sprintf("encountered unexpected kind %s for api %s", apiRes.Spec.Kind, apiRes.Spec.Name))
+		return "", errors.ErrorUnexpected(fmt.Sprintf("encountered unexpected kind %s for api %s", apiRes.Metadata.Kind, apiRes.Metadata.Name))
 	}
 }
 
diff --git a/cli/cmd/lib_traffic_splitters.go b/cli/cmd/lib_traffic_splitters.go
index af2b4e4aad..79313675fd 100644
--- a/cli/cmd/lib_traffic_splitters.go
+++ b/cli/cmd/lib_traffic_splitters.go
@@ -80,7 +80,7 @@ func trafficSplitTable(trafficSplitter schema.APIResponse, env cliconfig.Environ
 		}
 		lastUpdated := time.Unix(apiRes.Metadata.LastUpdated, 0)
 
-		apiName := apiRes.Spec.Name
+		apiName := apiRes.Metadata.Name
 		if api.Shadow {
 			apiName += " (shadow)"
 		}


Test patch: