235 lines
6.9 KiB
Go
235 lines
6.9 KiB
Go
// ABOUTME: Application instance lifecycle management APIs for EdgeXR Master Controller
|
|
// ABOUTME: Provides typed methods for creating, querying, and deleting application instances
|
|
|
|
package edgeconnect
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
sdkhttp "edp.buildth.ing/DevFW-CICD/edge-connect-client/sdk/internal/http"
|
|
)
|
|
|
|
// CreateAppInstance creates a new application instance in the specified region
|
|
// Maps to POST /auth/ctrl/CreateAppInst
|
|
func (c *Client) CreateAppInstance(ctx context.Context, input *NewAppInstanceInput) error {
|
|
transport := c.getTransport()
|
|
url := c.BaseURL + "/api/v1/auth/ctrl/CreateAppInst"
|
|
|
|
resp, err := transport.Call(ctx, "POST", url, input)
|
|
if err != nil {
|
|
return fmt.Errorf("CreateAppInstance failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode >= 400 {
|
|
return c.handleErrorResponse(resp, "CreateAppInstance")
|
|
}
|
|
|
|
c.logf("CreateAppInstance: %s/%s created successfully",
|
|
input.AppInst.Key.Organization, input.AppInst.Key.Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// ShowAppInstance retrieves a single application instance by key and region
|
|
// Maps to POST /auth/ctrl/ShowAppInst
|
|
func (c *Client) ShowAppInstance(ctx context.Context, appInstKey AppInstanceKey, appKey AppKey, region string) (AppInstance, error) {
|
|
transport := c.getTransport()
|
|
url := c.BaseURL + "/api/v1/auth/ctrl/ShowAppInst"
|
|
|
|
filter := AppInstanceFilter{
|
|
AppInstance: AppInstance{AppKey: appKey, Key: appInstKey},
|
|
Region: region,
|
|
}
|
|
|
|
resp, err := transport.Call(ctx, "POST", url, filter)
|
|
if err != nil {
|
|
return AppInstance{}, fmt.Errorf("ShowAppInstance failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == http.StatusNotFound {
|
|
return AppInstance{}, fmt.Errorf("app instance %s/%s: %w",
|
|
appInstKey.Organization, appInstKey.Name, ErrResourceNotFound)
|
|
}
|
|
|
|
if resp.StatusCode >= 400 {
|
|
return AppInstance{}, c.handleErrorResponse(resp, "ShowAppInstance")
|
|
}
|
|
|
|
// Parse streaming JSON response
|
|
var appInstances []AppInstance
|
|
if err := c.parseStreamingAppInstanceResponse(resp, &appInstances); err != nil {
|
|
return AppInstance{}, fmt.Errorf("ShowAppInstance failed to parse response: %w", err)
|
|
}
|
|
|
|
if len(appInstances) == 0 {
|
|
return AppInstance{}, fmt.Errorf("app instance %s/%s in region %s: %w",
|
|
appInstKey.Organization, appInstKey.Name, region, ErrResourceNotFound)
|
|
}
|
|
|
|
return appInstances[0], nil
|
|
}
|
|
|
|
// ShowAppInstances retrieves all application instances matching the filter criteria
|
|
// Maps to POST /auth/ctrl/ShowAppInst
|
|
func (c *Client) ShowAppInstances(ctx context.Context, appInstKey AppInstanceKey, region string) ([]AppInstance, error) {
|
|
transport := c.getTransport()
|
|
url := c.BaseURL + "/api/v1/auth/ctrl/ShowAppInst"
|
|
|
|
filter := AppInstanceFilter{
|
|
AppInstance: AppInstance{Key: appInstKey},
|
|
Region: region,
|
|
}
|
|
|
|
resp, err := transport.Call(ctx, "POST", url, filter)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ShowAppInstances failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound {
|
|
return nil, c.handleErrorResponse(resp, "ShowAppInstances")
|
|
}
|
|
|
|
var appInstances []AppInstance
|
|
if resp.StatusCode == http.StatusNotFound {
|
|
return appInstances, nil // Return empty slice for not found
|
|
}
|
|
|
|
if err := c.parseStreamingAppInstanceResponse(resp, &appInstances); err != nil {
|
|
return nil, fmt.Errorf("ShowAppInstances failed to parse response: %w", err)
|
|
}
|
|
|
|
c.logf("ShowAppInstances: found %d app instances matching criteria", len(appInstances))
|
|
return appInstances, nil
|
|
}
|
|
|
|
// UpdateAppInstance updates an application instance and then refreshes it
|
|
// Maps to POST /auth/ctrl/UpdateAppInst
|
|
func (c *Client) UpdateAppInstance(ctx context.Context, input *UpdateAppInstanceInput) error {
|
|
transport := c.getTransport()
|
|
url := c.BaseURL + "/api/v1/auth/ctrl/UpdateAppInst"
|
|
|
|
resp, err := transport.Call(ctx, "POST", url, input)
|
|
if err != nil {
|
|
return fmt.Errorf("UpdateAppInstance failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode >= 400 {
|
|
return c.handleErrorResponse(resp, "UpdateAppInstance")
|
|
}
|
|
|
|
c.logf("UpdateAppInstance: %s/%s updated successfully",
|
|
input.AppInst.Key.Organization, input.AppInst.Key.Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// RefreshAppInstance refreshes an application instance's state
|
|
// Maps to POST /auth/ctrl/RefreshAppInst
|
|
func (c *Client) RefreshAppInstance(ctx context.Context, appInstKey AppInstanceKey, region string) error {
|
|
transport := c.getTransport()
|
|
url := c.BaseURL + "/api/v1/auth/ctrl/RefreshAppInst"
|
|
|
|
filter := AppInstanceFilter{
|
|
AppInstance: AppInstance{Key: appInstKey},
|
|
Region: region,
|
|
}
|
|
|
|
resp, err := transport.Call(ctx, "POST", url, filter)
|
|
if err != nil {
|
|
return fmt.Errorf("RefreshAppInstance failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode >= 400 {
|
|
return c.handleErrorResponse(resp, "RefreshAppInstance")
|
|
}
|
|
|
|
c.logf("RefreshAppInstance: %s/%s refreshed successfully",
|
|
appInstKey.Organization, appInstKey.Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteAppInstance removes an application instance from the specified region
|
|
// Maps to POST /auth/ctrl/DeleteAppInst
|
|
func (c *Client) DeleteAppInstance(ctx context.Context, appInstKey AppInstanceKey, region string) error {
|
|
transport := c.getTransport()
|
|
url := c.BaseURL + "/api/v1/auth/ctrl/DeleteAppInst"
|
|
|
|
filter := AppInstanceFilter{
|
|
AppInstance: AppInstance{Key: appInstKey},
|
|
Region: region,
|
|
}
|
|
|
|
resp, err := transport.Call(ctx, "POST", url, filter)
|
|
if err != nil {
|
|
return fmt.Errorf("DeleteAppInstance failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// 404 is acceptable for delete operations (already deleted)
|
|
if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound {
|
|
return c.handleErrorResponse(resp, "DeleteAppInstance")
|
|
}
|
|
|
|
c.logf("DeleteAppInstance: %s/%s deleted successfully",
|
|
appInstKey.Organization, appInstKey.Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// parseStreamingAppInstanceResponse parses the EdgeXR streaming JSON response format for app instances
|
|
func (c *Client) parseStreamingAppInstanceResponse(resp *http.Response, result interface{}) error {
|
|
var responses []Response[AppInstance]
|
|
|
|
parseErr := sdkhttp.ParseJSONLines(resp.Body, func(line []byte) error {
|
|
var response Response[AppInstance]
|
|
if err := json.Unmarshal(line, &response); err != nil {
|
|
return err
|
|
}
|
|
responses = append(responses, response)
|
|
return nil
|
|
})
|
|
|
|
if parseErr != nil {
|
|
return parseErr
|
|
}
|
|
|
|
// Extract data from responses
|
|
var appInstances []AppInstance
|
|
var messages []string
|
|
|
|
for _, response := range responses {
|
|
if response.HasData() {
|
|
appInstances = append(appInstances, response.Data)
|
|
}
|
|
if response.IsMessage() {
|
|
messages = append(messages, response.Data.GetMessage())
|
|
}
|
|
}
|
|
|
|
// If we have error messages, return them
|
|
if len(messages) > 0 {
|
|
return &APIError{
|
|
StatusCode: resp.StatusCode,
|
|
Messages: messages,
|
|
}
|
|
}
|
|
|
|
// Set result based on type
|
|
switch v := result.(type) {
|
|
case *[]AppInstance:
|
|
*v = appInstances
|
|
default:
|
|
return fmt.Errorf("unsupported result type: %T", result)
|
|
}
|
|
|
|
return nil
|
|
}
|