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 }