github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/provisioner/client.go (about)

     1  package provisioner
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  
     8  	kebError "github.com/kyma-project/kyma-environment-broker/internal/error"
     9  	"github.com/kyma-project/kyma-environment-broker/internal/httputil"
    10  
    11  	schema "github.com/kyma-project/control-plane/components/provisioner/pkg/gqlschema"
    12  	gcli "github.com/kyma-project/kyma-environment-broker/internal/third_party/machinebox/graphql"
    13  )
    14  
    15  // accountIDKey is a header key name for request send by graphQL client
    16  const (
    17  	accountIDKey    = "tenant"
    18  	subAccountIDKey = "sub-account"
    19  )
    20  
    21  //go:generate mockery --name=Client --output=automock --outpkg=automock --case=underscore
    22  
    23  type Client interface {
    24  	ProvisionRuntime(accountID, subAccountID string, config schema.ProvisionRuntimeInput) (schema.OperationStatus, error)
    25  	DeprovisionRuntime(accountID, runtimeID string) (string, error)
    26  	UpgradeRuntime(accountID, runtimeID string, config schema.UpgradeRuntimeInput) (schema.OperationStatus, error)
    27  	UpgradeShoot(accountID, runtimeID string, config schema.UpgradeShootInput) (schema.OperationStatus, error)
    28  	ReconnectRuntimeAgent(accountID, runtimeID string) (string, error)
    29  	RuntimeOperationStatus(accountID, operationID string) (schema.OperationStatus, error)
    30  	RuntimeStatus(accountID, runtimeID string) (schema.RuntimeStatus, error)
    31  }
    32  
    33  type client struct {
    34  	graphQLClient *gcli.Client
    35  	queryProvider queryProvider
    36  	graphqlizer   Graphqlizer
    37  }
    38  
    39  func NewProvisionerClient(endpoint string, queryDumping bool) Client {
    40  	graphQlClient := gcli.NewClient(endpoint, gcli.WithHTTPClient(httputil.NewClient(120, false)))
    41  	if queryDumping {
    42  		graphQlClient.Log = func(s string) {
    43  			fmt.Println(s)
    44  		}
    45  	}
    46  
    47  	return &client{
    48  		graphQLClient: graphQlClient,
    49  		queryProvider: queryProvider{},
    50  		graphqlizer:   Graphqlizer{},
    51  	}
    52  }
    53  
    54  func (c *client) ProvisionRuntime(accountID, subAccountID string, config schema.ProvisionRuntimeInput) (schema.OperationStatus, error) {
    55  	provisionRuntimeIptGQL, err := c.graphqlizer.ProvisionRuntimeInputToGraphQL(config)
    56  	if err != nil {
    57  		return schema.OperationStatus{}, fmt.Errorf("failed to convert Provision Runtime Input to query: %w", err)
    58  	}
    59  
    60  	query := c.queryProvider.provisionRuntime(provisionRuntimeIptGQL)
    61  	req := gcli.NewRequest(query)
    62  	req.Header.Add(accountIDKey, accountID)
    63  	req.Header.Add(subAccountIDKey, subAccountID)
    64  
    65  	var response schema.OperationStatus
    66  	err = c.executeRequest(req, &response)
    67  	if err != nil {
    68  		return schema.OperationStatus{}, fmt.Errorf("failed to provision a Runtime: %w", err)
    69  	}
    70  
    71  	return response, nil
    72  }
    73  
    74  func (c *client) DeprovisionRuntime(accountID, runtimeID string) (string, error) {
    75  	query := c.queryProvider.deprovisionRuntime(runtimeID)
    76  	req := gcli.NewRequest(query)
    77  	req.Header.Add(accountIDKey, accountID)
    78  
    79  	var operationId string
    80  	err := c.executeRequest(req, &operationId)
    81  	if err != nil {
    82  		return "", fmt.Errorf("failed to deprovision Runtime: %w", err)
    83  	}
    84  	return operationId, nil
    85  }
    86  
    87  func (c *client) UpgradeRuntime(accountID, runtimeID string, config schema.UpgradeRuntimeInput) (schema.OperationStatus, error) {
    88  	upgradeRuntimeIptGQL, err := c.graphqlizer.UpgradeRuntimeInputToGraphQL(config)
    89  	if err != nil {
    90  		return schema.OperationStatus{}, fmt.Errorf("failed to convert Upgrade Runtime Input to query: %w", err)
    91  	}
    92  
    93  	query := c.queryProvider.upgradeRuntime(runtimeID, upgradeRuntimeIptGQL)
    94  	req := gcli.NewRequest(query)
    95  	req.Header.Add(accountIDKey, accountID)
    96  
    97  	var res schema.OperationStatus
    98  	err = c.executeRequest(req, &res)
    99  	if err != nil {
   100  		return schema.OperationStatus{}, fmt.Errorf("failed to upgrade Runtime: %w", err)
   101  	}
   102  	return res, nil
   103  }
   104  
   105  func (c *client) UpgradeShoot(accountID, runtimeID string, config schema.UpgradeShootInput) (schema.OperationStatus, error) {
   106  	upgradeShootIptGQL, err := c.graphqlizer.UpgradeShootInputToGraphQL(config)
   107  	if err != nil {
   108  		return schema.OperationStatus{}, fmt.Errorf("failed to convert Upgrade Shoot Input to query: %w", err)
   109  	}
   110  
   111  	query := c.queryProvider.upgradeShoot(runtimeID, upgradeShootIptGQL)
   112  	req := gcli.NewRequest(query)
   113  	req.Header.Add(accountIDKey, accountID)
   114  
   115  	var res schema.OperationStatus
   116  	err = c.executeRequest(req, &res)
   117  	if err != nil {
   118  		return schema.OperationStatus{}, fmt.Errorf("failed to upgrade Shoot: %w", err)
   119  	}
   120  	return res, nil
   121  }
   122  
   123  func (c *client) ReconnectRuntimeAgent(accountID, runtimeID string) (string, error) {
   124  	query := c.queryProvider.reconnectRuntimeAgent(runtimeID)
   125  	req := gcli.NewRequest(query)
   126  	req.Header.Add(accountIDKey, accountID)
   127  
   128  	var operationId string
   129  	err := c.executeRequest(req, &operationId)
   130  	if err != nil {
   131  		return "", fmt.Errorf("failed to reconnect Runtime agent: %w", err)
   132  	}
   133  	return operationId, nil
   134  }
   135  
   136  func (c *client) RuntimeOperationStatus(accountID, operationID string) (schema.OperationStatus, error) {
   137  	query := c.queryProvider.runtimeOperationStatus(operationID)
   138  	req := gcli.NewRequest(query)
   139  	req.Header.Add(accountIDKey, accountID)
   140  
   141  	var response schema.OperationStatus
   142  	err := c.executeRequest(req, &response)
   143  	if err != nil {
   144  		return schema.OperationStatus{}, fmt.Errorf("failed to get Runtime operation status: %w", err)
   145  	}
   146  	return response, nil
   147  }
   148  
   149  func (c *client) RuntimeStatus(accountID, runtimeID string) (schema.RuntimeStatus, error) {
   150  	query := c.queryProvider.runtimeStatus(runtimeID)
   151  	req := gcli.NewRequest(query)
   152  	req.Header.Add(accountIDKey, accountID)
   153  
   154  	var response schema.RuntimeStatus
   155  	err := c.executeRequest(req, &response)
   156  	if err != nil {
   157  		return schema.RuntimeStatus{}, fmt.Errorf("failed to get Runtime status: %w", err)
   158  	}
   159  	return response, nil
   160  }
   161  
   162  func (c *client) executeRequest(req *gcli.Request, respDestination interface{}) error {
   163  	if reflect.ValueOf(respDestination).Kind() != reflect.Ptr {
   164  		return fmt.Errorf("destination is not of pointer type")
   165  	}
   166  
   167  	type graphQLResponseWrapper struct {
   168  		Result interface{} `json:"result"`
   169  	}
   170  
   171  	wrapper := &graphQLResponseWrapper{Result: respDestination}
   172  	err := c.graphQLClient.Run(context.TODO(), req, wrapper)
   173  	switch {
   174  	case isNotFoundError(err):
   175  		return kebError.NotFoundError{}
   176  	case isClientError(err):
   177  		return err
   178  	case err != nil:
   179  		return kebError.WrapAsTemporaryError(err, "failed to execute the request")
   180  	}
   181  
   182  	return nil
   183  }
   184  
   185  func isClientError(err error) bool {
   186  	if ee, ok := err.(gcli.ExtendedError); ok {
   187  		code, found := ee.Extensions()["error_code"]
   188  		if found {
   189  			errCode := code.(float64)
   190  			if errCode >= 400 && errCode < 500 {
   191  				return true
   192  			}
   193  		}
   194  	}
   195  	return false
   196  }
   197  
   198  func isNotFoundError(err error) bool {
   199  	if ee, ok := err.(gcli.ExtendedError); ok {
   200  		reason, found := ee.Extensions()["error_reason"]
   201  		if found {
   202  			if reason == "err_db_not_found" {
   203  				return true
   204  			}
   205  		}
   206  	}
   207  	return false
   208  }
   209  
   210  func OperationStatusLastError(lastErr *schema.LastError) kebError.ErrorReporter {
   211  	var err kebError.LastError
   212  
   213  	if lastErr == nil {
   214  		return err.SetReason(kebError.ErrProvisionerNilLastError).SetComponent(kebError.ErrProvisioner)
   215  	}
   216  
   217  	return err.SetMessage(lastErr.ErrMessage).SetReason(kebError.ErrReason(lastErr.Reason)).SetComponent(kebError.ErrComponent(lastErr.Component))
   218  }