diff --git a/internal/client/client.go b/internal/client/client.go index 95278d2..7ff7762 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "fmt" - "io" "log" "net/http" @@ -22,305 +21,6 @@ type Credentials struct { Password string } -// curl -X POST https://mc.orca.platform.mg3.mdb.osc.live/api/v1/auth/ctrl/CreateAppInst -H 'Content-Type: application/json' -H "Authorization: Bearer $EDGEXR_TOKEN" -S --data "$CREATEAPPINSTANCE_JSON" --fail-with-body - -func (e *EdgeConnect) NewAppInstance(ctx context.Context, input NewAppInstanceInput) error { - token, err := e.RetrieveToken(ctx) - if err != nil { - log.Printf("failed to retrieve token %v\n", err) - return err - } - - json_data, err := json.Marshal(input) - if err != nil { - log.Printf("failed to marshal NewAppInstanceInput %v\n", err) - return err - } - - request, err := http.NewRequestWithContext(ctx, "POST", e.BaseURL+"/api/v1/auth/ctrl/CreateAppInst", bytes.NewBuffer(json_data)) - if err != nil { - log.Printf("failed to create request %v\n", err) - return err - } - request.Header.Set("Content-Type", "application/json") - request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - - resp, err := e.HttpClient.Do(request) - if err != nil { - log.Printf("failed to execute request %v\n", err) - return err - } - defer resp.Body.Close() - - bytes, err := io.ReadAll(resp.Body) - if err != nil { - log.Printf("err while io.ReadAll: %v\n", err) - return err - } - - log.Printf("Body %v\n", string(bytes)) - - log.Printf("status code %v\n", resp.Status) - - return nil -} - -func (e *EdgeConnect) NewApp(ctx context.Context, input NewAppInput) error { - token, err := e.RetrieveToken(ctx) - if err != nil { - log.Printf("err while RetrieveToken: %v\n", err) - return err - } - - json_data, err := json.Marshal(input) - if err != nil { - log.Printf("err while Marshal: %v\n", err) - return err - } - - request, err := http.NewRequestWithContext(ctx, "POST", e.BaseURL+"/api/v1/auth/ctrl/CreateApp", bytes.NewBuffer(json_data)) - if err != nil { - log.Printf("err while NewRequestWithContext: %v\n", err) - return err - } - request.Header.Set("Content-Type", "application/json") - request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - - resp, err := e.HttpClient.Do(request) - if err != nil { - log.Printf("err while HttpClient.Do: %v\n", err) - return err - } - - defer resp.Body.Close() - - log.Printf("status code %v\n", resp.Status) - bytes, err := io.ReadAll(resp.Body) - if err != nil { - log.Printf("err while io.ReadAll: %v\n", err) - return err - } - - log.Printf("Body %v\n", string(bytes)) - - return nil -} - -func (e *EdgeConnect) ShowApp(ctx context.Context, appkey AppKey, region string) (App, error) { - token, err := e.RetrieveToken(ctx) - if err != nil { - return App{}, err - } - - input := struct { - App struct { - Key AppKey `json:"key"` - } `json:"App"` - Region string `json:"Region"` - }{ - App: struct { - Key AppKey `json:"key"` - }{ - Key: appkey, - }, - Region: region, - } - - json_data, err := json.Marshal(input) - if err != nil { - return App{}, err - } - - request, err := http.NewRequestWithContext(ctx, "POST", e.BaseURL+"/api/v1/auth/ctrl/ShowApp", bytes.NewBuffer(json_data)) - if err != nil { - return App{}, err - } - request.Header.Set("Content-Type", "application/json") - request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - - resp, err := e.HttpClient.Do(request) - if err != nil { - return App{}, err - } - - defer resp.Body.Close() - var response struct { - Data App `json:"data"` - } - err = json.NewDecoder(resp.Body).Decode(&response) - if err != nil { - return App{}, err - } - - return response.Data, nil -} - -func (e *EdgeConnect) ShowApps(ctx context.Context, appkey AppKey, region string) ([]App, error) { - token, err := e.RetrieveToken(ctx) - if err != nil { - return []App{}, err - } - - input := struct { - App struct { - Key AppKey `json:"key"` - } `json:"App"` - Region string `json:"Region"` - }{ - App: struct { - Key AppKey `json:"key"` - }{ - Key: appkey, - }, - Region: region, - } - - json_data, err := json.Marshal(input) - if err != nil { - return []App{}, err - } - - request, err := http.NewRequestWithContext(ctx, "POST", e.BaseURL+"/api/v1/auth/ctrl/ShowApp", bytes.NewBuffer(json_data)) - if err != nil { - return []App{}, err - } - request.Header.Set("Content-Type", "application/json") - request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - - resp, err := e.HttpClient.Do(request) - if err != nil { - return []App{}, err - } - - defer resp.Body.Close() - type response struct { - Data App `json:"data"` - } - - apps := []App{} - decoder := json.NewDecoder(resp.Body) - for { - var d response - if err := decoder.Decode(&d); err != nil { - if err.Error() == "EOF" { - break - } - log.Fatal(err) - } - apps = append(apps, d.Data) - } - - return apps, nil -} - -func (e *EdgeConnect) ShowAppInstance(ctx context.Context, appinstkey AppInstanceKey, region string) (AppInstance, error) { - token, err := e.RetrieveToken(ctx) - if err != nil { - return AppInstance{}, err - } - - input := struct { - App struct { - AppInstKey AppInstanceKey `json:"key"` - } `json:"appinst"` - Region string `json:"Region"` - }{ - App: struct { - AppInstKey AppInstanceKey `json:"key"` - }{ - AppInstKey: appinstkey, - }, - Region: region, - } - - json_data, err := json.Marshal(input) - if err != nil { - return AppInstance{}, err - } - - request, err := http.NewRequestWithContext(ctx, "POST", e.BaseURL+"/api/v1/auth/ctrl/ShowAppInst", bytes.NewBuffer(json_data)) - if err != nil { - return AppInstance{}, err - } - request.Header.Set("Content-Type", "application/json") - request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - - resp, err := e.HttpClient.Do(request) - if err != nil { - return AppInstance{}, err - } - - defer resp.Body.Close() - var response struct { - Data AppInstance `json:"data"` - } - - err = json.NewDecoder(resp.Body).Decode(&response) - if err != nil { - return AppInstance{}, err - } - - return response.Data, nil -} - -func (e *EdgeConnect) ShowAppInstances(ctx context.Context, appinstkey AppInstanceKey, region string) ([]AppInstance, error) { - token, err := e.RetrieveToken(ctx) - if err != nil { - return []AppInstance{}, err - } - - input := struct { - App struct { - AppInstKey AppInstanceKey `json:"key"` - } `json:"appinst"` - Region string `json:"Region"` - }{ - App: struct { - AppInstKey AppInstanceKey `json:"key"` - }{ - AppInstKey: appinstkey, - }, - Region: region, - } - - json_data, err := json.Marshal(input) - if err != nil { - return []AppInstance{}, err - } - - request, err := http.NewRequestWithContext(ctx, "POST", e.BaseURL+"/api/v1/auth/ctrl/ShowAppInst", bytes.NewBuffer(json_data)) - if err != nil { - return []AppInstance{}, err - } - request.Header.Set("Content-Type", "application/json") - request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - - resp, err := e.HttpClient.Do(request) - if err != nil { - return []AppInstance{}, err - } - - defer resp.Body.Close() - type response struct { - Data AppInstance `json:"data"` - } - - appinstances := []AppInstance{} - decoder := json.NewDecoder(resp.Body) - for { - var d response - if err := decoder.Decode(&d); err != nil { - if err.Error() == "EOF" { - break - } - log.Fatal(err) - } - appinstances = append(appinstances, d.Data) - } - - return appinstances, nil -} - func (e *EdgeConnect) RetrieveToken(ctx context.Context) (string, error) { json_data, err := json.Marshal(map[string]string{ "username": e.Credentials.Username, @@ -354,23 +54,83 @@ func (e *EdgeConnect) RetrieveToken(ctx context.Context) (string, error) { return respData.Token, nil } -func (e *EdgeConnect) DeleteApp(ctx context.Context, appkey AppKey, region string) error { - token, err := e.RetrieveToken(ctx) +func (e *EdgeConnect) CreateApp(ctx context.Context, input NewAppInput) error { + json_data, err := json.Marshal(input) if err != nil { return err } + response, err := call[App](ctx, e, "/api/v1/auth/ctrl/CreateApp", json_data) + if err != nil { + return err + } + + return response.Error() +} + +func (e *EdgeConnect) ShowApp(ctx context.Context, appkey AppKey, region string) (App, error) { input := struct { - App struct { - Key AppKey `json:"key"` - } `json:"App"` + App App `json:"App"` Region string `json:"Region"` }{ - App: struct { - Key AppKey `json:"key"` - }{ - Key: appkey, - }, + App: App{Key: appkey}, + Region: region, + } + + json_data, err := json.Marshal(input) + if err != nil { + return App{}, err + } + + responses, err := call[App](ctx, e, "/api/v1/auth/ctrl/ShowApp", json_data) + if err != nil { + return App{}, err + } + + if !responses.IsSuccessful() { + return App{}, responses.Error() + } + + apps := responses.GetData() + if len(apps) > 0 { + return apps[0], nil + } + + return App{}, fmt.Errorf("could not find app with region/key: %s/%v", region, appkey) +} + +func (e *EdgeConnect) ShowApps(ctx context.Context, appkey AppKey, region string) ([]App, error) { + input := struct { + App App `json:"App"` + Region string `json:"Region"` + }{ + App: App{Key: appkey}, + Region: region, + } + + json_data, err := json.Marshal(input) + if err != nil { + return nil, err + } + + responses, err := call[App](ctx, e, "/api/v1/auth/ctrl/ShowApp", json_data) + if err != nil { + return nil, err + } + + if !responses.IsSuccessful() { + return nil, responses.Error() + } + + return responses.GetData(), nil +} + +func (e *EdgeConnect) DeleteApp(ctx context.Context, appkey AppKey, region string) error { + input := struct { + App App `json:"App"` + Region string `json:"Region"` + }{ + App: App{Key: appkey}, Region: region, } @@ -379,46 +139,97 @@ func (e *EdgeConnect) DeleteApp(ctx context.Context, appkey AppKey, region strin return err } - request, err := http.NewRequestWithContext(ctx, "POST", e.BaseURL+"/api/v1/auth/ctrl/DeleteApp", bytes.NewBuffer(json_data)) - if err != nil { - return err - } - request.Header.Set("Content-Type", "application/json") - request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - - resp, err := e.HttpClient.Do(request) + response, err := call[App](ctx, e, "/api/v1/auth/ctrl/DeleteApp", json_data) if err != nil { return err } - defer resp.Body.Close() - /*bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return err + if !response.IsSuccessful() && response.StatusCode != 404 { + return response.Error() } - fmt.Printf("Response: %v\n", string(bodyBytes))*/ return nil } +func (e *EdgeConnect) CreateAppInstance(ctx context.Context, input NewAppInstanceInput) error { + json_data, err := json.Marshal(input) + if err != nil { + log.Printf("failed to marshal NewAppInstanceInput %v\n", err) + return err + } + + responses, err := call[AppInstance](ctx, e, "/api/v1/auth/ctrl/CreateAppInst", json_data) + if err != nil { + return err + } + + return responses.Error() +} + +func (e *EdgeConnect) ShowAppInstance(ctx context.Context, appinstkey AppInstanceKey, region string) (AppInstance, error) { + input := struct { + App AppInstance `json:"appinst"` + Region string `json:"Region"` + }{ + App: AppInstance{Key: appinstkey}, + Region: region, + } + + json_data, err := json.Marshal(input) + if err != nil { + return AppInstance{}, err + } + + responses, err := call[AppInstance](ctx, e, "/api/v1/auth/ctrl/ShowAppInst", json_data) + if err != nil { + return AppInstance{}, err + } + + if !responses.IsSuccessful() { + return AppInstance{}, responses.Error() + } + + data := responses.GetData() + if len(data) > 0 { + return data[0], nil + } + + return AppInstance{}, fmt.Errorf("could not find app: %v", responses) +} + +func (e *EdgeConnect) ShowAppInstances(ctx context.Context, appinstkey AppInstanceKey, region string) ([]AppInstance, error) { + input := struct { + App AppInstance `json:"appinst"` + Region string `json:"Region"` + }{ + App: AppInstance{Key: appinstkey}, + Region: region, + } + + json_data, err := json.Marshal(input) + if err != nil { + return nil, err + } + + responses, err := call[AppInstance](ctx, e, "/api/v1/auth/ctrl/ShowAppInst", json_data) + if err != nil { + return nil, err + } + + if !responses.IsSuccessful() { + return nil, responses.Error() + } + + return responses.GetData(), nil +} + func (e *EdgeConnect) DeleteAppInstance(ctx context.Context, appinstancekey AppInstanceKey, region string) error { - token, err := e.RetrieveToken(ctx) - if err != nil { - return err - } - input := struct { - App struct { - AppInstKey AppInstanceKey `json:"key"` - } `json:"appinst"` - Region string `json:"Region"` + AppInstance AppInstance `json:"appinst"` + Region string `json:"Region"` }{ - App: struct { - AppInstKey AppInstanceKey `json:"key"` - }{ - AppInstKey: appinstancekey, - }, - Region: region, + AppInstance: AppInstance{Key: appinstancekey}, + Region: region, } json_data, err := json.Marshal(input) @@ -426,25 +237,48 @@ func (e *EdgeConnect) DeleteAppInstance(ctx context.Context, appinstancekey AppI return err } - request, err := http.NewRequestWithContext(ctx, "POST", e.BaseURL+"/api/v1/auth/ctrl/DeleteAppInst", bytes.NewBuffer(json_data)) + responses, err := call[AppInstance](ctx, e, "/api/v1/auth/ctrl/DeleteAppInst", json_data) if err != nil { return err } + return responses.Error() +} + +func call[T any](ctx context.Context, client *EdgeConnect, path string, body []byte) (Responses[T], error) { + token, err := client.RetrieveToken(ctx) + if err != nil { + return Responses[T]{}, err + } + + request, err := http.NewRequestWithContext(ctx, "POST", path, bytes.NewBuffer(body)) + if err != nil { + return Responses[T]{}, err + } request.Header.Set("Content-Type", "application/json") request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - resp, err := e.HttpClient.Do(request) + resp, err := client.HttpClient.Do(request) if err != nil { - return err + return Responses[T]{}, err } - defer resp.Body.Close() - /*bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - fmt.Printf("Response: %v%v\n", resp.StatusCode, string(bodyBytes))*/ - return nil + responses := Responses[T]{} + responses.StatusCode = resp.StatusCode + decoder := json.NewDecoder(resp.Body) + for { + var d Response[T] + if err := decoder.Decode(&d); err != nil { + if err.Error() == "EOF" { + break + } + log.Fatal(err) + } + responses.Responses = append(responses.Responses, d) + } + + log.Printf("call: %s resulting in %v and %v messages", path, resp.StatusCode, len(responses.Responses)) + + return responses, nil } diff --git a/internal/client/models.go b/internal/client/models.go index fec1df6..29b1f1d 100644 --- a/internal/client/models.go +++ b/internal/client/models.go @@ -1,5 +1,57 @@ package client +import "fmt" + +type Responses[T any] struct { + Responses []Response[T] + StatusCode int +} + +func (r *Responses[T]) GetData() []T { + var data []T + for _, v := range r.Responses { + if v.HasData() { + data = append(data, *v.Data) + } + } + return data +} + +func (r *Responses[T]) GetMessages() []string { + var messages []string + for _, v := range r.Responses { + if v.HasData() { + messages = append(messages, v.Message) + } + } + return messages +} + +func (r *Responses[T]) IsSuccessful() bool { + return r.StatusCode < 400 && r.StatusCode > 0 +} + +func (r *Responses[T]) Error() error { + if r.IsSuccessful() { + return nil + } + + return fmt.Errorf("error with status code %v and messages %v", r.StatusCode, r.GetMessages()) +} + +type Response[T any] struct { + Data *T `json:"data"` + Message string `json:"message"` +} + +func (res *Response[T]) HasData() bool { + return res.Data != nil +} + +func (res *Response[T]) IsMessage() bool { + return res.Message != "" +} + type NewAppInstanceInput struct { Region string `json:"region"` AppInst AppInstance `json:"appinst"` @@ -7,8 +59,8 @@ type NewAppInstanceInput struct { type AppInstance struct { Key AppInstanceKey `json:"key"` - AppKey AppKey `json:"app_key"` - Flavor Flavor `json:"flavor"` + AppKey AppKey `json:"app_key,omitzero"` + Flavor Flavor `json:"flavor,omitzero"` State string `json:"state,omitempty"` PowerState string `json:"power_state,omitempty"` } @@ -41,12 +93,12 @@ type NewAppInput struct { type App struct { Key AppKey `json:"key"` - Deployment string `json:"deployment"` - ImageType string `json:"image_type"` - ImagePath string `json:"image_path"` - AllowServerless bool `json:"allow_serverless"` - DefaultFlavor Flavor `json:"defaultFlavor"` - ServerlessConfig any `json:"serverless_config"` - DeploymentGenerator string `json:"deployment_generator"` - DeploymentManifest string `json:"deployment_manifest"` + Deployment string `json:"deployment,omitempty"` + ImageType string `json:"image_type,omitempty"` + ImagePath string `json:"image_path,omitempty"` + AllowServerless bool `json:"allow_serverless,omitempty"` + DefaultFlavor Flavor `json:"defaultFlavor,omitempty"` + ServerlessConfig any `json:"serverless_config,omitempty"` + DeploymentGenerator string `json:"deployment_generator,omitempty"` + DeploymentManifest string `json:"deployment_manifest,omitempty"` } diff --git a/provider/provider.go b/provider/provider.go index f31a5c6..7d6f33c 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -135,7 +135,7 @@ func (a *edgeConnectProvider) CreateInstance(ctx context.Context, bootstrapParam manifest := fmt.Sprintf("%s\n---\n%s", string(podjson), string(servicejson)) - err = a.client.NewApp(ctx, client.NewAppInput{ + err = a.client.CreateApp(ctx, client.NewAppInput{ Region: a.cfg.Region, App: client.App{ Key: client.AppKey{ @@ -159,7 +159,7 @@ func (a *edgeConnectProvider) CreateInstance(ctx context.Context, bootstrapParam return params.ProviderInstance{}, err } - err = a.client.NewAppInstance(ctx, client.NewAppInstanceInput{ + err = a.client.CreateAppInstance(ctx, client.NewAppInstanceInput{ Region: a.cfg.Region, AppInst: client.AppInstance{ Key: client.AppInstanceKey{