github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/reconciler/client.go (about) 1 package reconciler 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/http" 10 "time" 11 12 reconcilerApi "github.com/kyma-incubator/reconciler/pkg/keb" 13 kebError "github.com/kyma-project/kyma-environment-broker/internal/error" 14 "github.com/sirupsen/logrus" 15 ) 16 17 //go:generate mockery --name=Client --output=automock --outpkg=automock --case=underscore 18 19 type Client interface { 20 ApplyClusterConfig(cluster reconcilerApi.Cluster) (*reconcilerApi.HTTPClusterResponse, error) 21 DeleteCluster(clusterName string) error 22 GetCluster(clusterName string, configVersion int64) (*reconcilerApi.HTTPClusterResponse, error) 23 GetLatestCluster(clusterName string) (*reconcilerApi.HTTPClusterResponse, error) 24 GetStatusChange(clusterName, offset string) ([]*reconcilerApi.StatusChange, error) 25 } 26 27 type Config struct { 28 URL string 29 ProvisioningTimeout time.Duration `json:"default=2h"` 30 } 31 32 type client struct { 33 httpClient *http.Client 34 log logrus.FieldLogger 35 config *Config 36 } 37 38 func NewReconcilerClient(httpClient *http.Client, log logrus.FieldLogger, cfg *Config) *client { 39 return &client{ 40 httpClient: httpClient, 41 log: log, 42 config: cfg, 43 } 44 } 45 46 // POST /v1/clusters 47 func (c *client) ApplyClusterConfig(cluster reconcilerApi.Cluster) (*reconcilerApi.HTTPClusterResponse, error) { 48 reqBody, err := json.Marshal(cluster) 49 if err != nil { 50 c.log.Error(err) 51 return &reconcilerApi.HTTPClusterResponse{}, err 52 } 53 54 reader := bytes.NewReader(reqBody) 55 56 request, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/clusters", c.config.URL), reader) 57 if err != nil { 58 c.log.Error(err) 59 return &reconcilerApi.HTTPClusterResponse{}, err 60 } 61 62 res, err := c.httpClient.Do(request) 63 if err != nil { 64 c.log.Error(err) 65 return &reconcilerApi.HTTPClusterResponse{}, kebError.NewTemporaryError(err.Error()) 66 } 67 defer res.Body.Close() 68 69 c.log.Debugf("Got response: statusCode=%d", res.StatusCode) 70 switch { 71 case res.StatusCode == http.StatusOK || res.StatusCode == http.StatusCreated: 72 case res.StatusCode >= 400 && res.StatusCode < 500: 73 message, _ := io.ReadAll(res.Body) 74 logrus.Errorf("Reconciler response: %s", string(message)) 75 return nil, httpStatusCodeError(res.StatusCode) 76 case res.StatusCode >= 500: 77 return nil, kebError.WrapNewTemporaryError(httpStatusCodeError(res.StatusCode)) 78 } 79 80 registerClusterResponse, err := ioutil.ReadAll(res.Body) 81 if err != nil { 82 c.log.Error(err) 83 return &reconcilerApi.HTTPClusterResponse{}, err 84 } 85 var response *reconcilerApi.HTTPClusterResponse 86 err = json.Unmarshal(registerClusterResponse, &response) 87 if err != nil { 88 c.log.Error(err) 89 return &reconcilerApi.HTTPClusterResponse{}, err 90 } 91 return response, nil 92 } 93 94 // DELETE /v1/clusters/{clusterName} 95 func (c *client) DeleteCluster(clusterName string) error { 96 request, err := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/clusters/%s", c.config.URL, clusterName), nil) 97 if err != nil { 98 c.log.Error(err) 99 return err 100 } 101 102 res, err := c.httpClient.Do(request) 103 if err != nil { 104 c.log.Error(err) 105 return kebError.NewTemporaryError(err.Error()) 106 } 107 switch { 108 case res.StatusCode == http.StatusNotFound: 109 return nil 110 case res.StatusCode >= 400 && res.StatusCode < 500 && res.StatusCode != http.StatusNotFound: 111 return httpStatusCodeError(res.StatusCode) 112 case res.StatusCode >= 500: 113 return kebError.WrapNewTemporaryError(httpStatusCodeError(res.StatusCode)) 114 default: 115 return nil 116 } 117 118 } 119 120 // GET /v1/clusters/{clusterName}/configs/{configVersion}/status 121 func (c *client) GetCluster(clusterName string, configVersion int64) (*reconcilerApi.HTTPClusterResponse, error) { 122 request, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/clusters/%s/configs/%d/status", c.config.URL, clusterName, configVersion), nil) 123 if err != nil { 124 c.log.Error(err) 125 return &reconcilerApi.HTTPClusterResponse{}, err 126 } 127 128 res, err := c.httpClient.Do(request) 129 if err != nil { 130 c.log.Error(err) 131 return &reconcilerApi.HTTPClusterResponse{}, kebError.NewTemporaryError(err.Error()) 132 } 133 defer res.Body.Close() 134 switch { 135 case res.StatusCode == http.StatusNotFound: 136 return &reconcilerApi.HTTPClusterResponse{}, kebError.NotFoundError{} 137 case res.StatusCode >= 400 && res.StatusCode < 500 && res.StatusCode != http.StatusNotFound: 138 return &reconcilerApi.HTTPClusterResponse{}, httpStatusCodeError(res.StatusCode) 139 case res.StatusCode >= 500: 140 return &reconcilerApi.HTTPClusterResponse{}, kebError.WrapNewTemporaryError(httpStatusCodeError(res.StatusCode)) 141 } 142 143 getClusterResponse, err := ioutil.ReadAll(res.Body) 144 if err != nil { 145 c.log.Error(err) 146 return &reconcilerApi.HTTPClusterResponse{}, err 147 } 148 var response *reconcilerApi.HTTPClusterResponse 149 err = json.Unmarshal(getClusterResponse, &response) 150 if err != nil { 151 c.log.Error(err) 152 return &reconcilerApi.HTTPClusterResponse{}, err 153 } 154 return response, nil 155 } 156 157 // GET v1/clusters/{clusterName}/status 158 func (c *client) GetLatestCluster(clusterName string) (*reconcilerApi.HTTPClusterResponse, error) { 159 request, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/clusters/%s/status", c.config.URL, clusterName), nil) 160 if err != nil { 161 c.log.Error(err) 162 return &reconcilerApi.HTTPClusterResponse{}, err 163 } 164 165 res, err := c.httpClient.Do(request) 166 if err != nil { 167 c.log.Error(err) 168 return &reconcilerApi.HTTPClusterResponse{}, kebError.NewTemporaryError(err.Error()) 169 } 170 defer res.Body.Close() 171 172 getClusterResponse, err := ioutil.ReadAll(res.Body) 173 if err != nil { 174 c.log.Error(err) 175 return &reconcilerApi.HTTPClusterResponse{}, err 176 } 177 var response *reconcilerApi.HTTPClusterResponse 178 err = json.Unmarshal(getClusterResponse, &response) 179 if err != nil { 180 c.log.Error(err) 181 return &reconcilerApi.HTTPClusterResponse{}, err 182 } 183 return response, nil 184 } 185 186 // GET v1/clusters/{clusterName}/statusChanges/{offset} 187 // offset is parsed to time.Duration 188 func (c *client) GetStatusChange(clusterName, offset string) ([]*reconcilerApi.StatusChange, error) { 189 request, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/clusters/%s/statusChanges/%s", c.config.URL, clusterName, offset), nil) 190 if err != nil { 191 c.log.Error(err) 192 return []*reconcilerApi.StatusChange{}, err 193 } 194 195 res, err := c.httpClient.Do(request) 196 if err != nil { 197 c.log.Error(err) 198 return []*reconcilerApi.StatusChange{}, err 199 } 200 defer res.Body.Close() 201 202 getStatusChangeResponse, err := ioutil.ReadAll(res.Body) 203 if err != nil { 204 c.log.Error(err) 205 return []*reconcilerApi.StatusChange{}, err 206 } 207 var response []*reconcilerApi.StatusChange 208 err = json.Unmarshal(getStatusChangeResponse, &response) 209 if err != nil { 210 c.log.Error(err) 211 return []*reconcilerApi.StatusChange{}, err 212 } 213 return response, nil 214 } 215 216 func httpStatusCodeError(code int) kebError.LastError { 217 return kebError.LastError{}. 218 SetMessage(fmt.Sprintf("got status %d", code)). 219 SetReason(kebError.ErrHttpStatusCode). 220 SetComponent(kebError.ErrReconciler) 221 }