github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/kubeconfig/handler_test.go (about) 1 package kubeconfig 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "net/http/httptest" 9 "testing" 10 11 "github.com/kyma-project/kyma-environment-broker/internal/broker" 12 "github.com/stretchr/testify/assert" 13 14 "github.com/kyma-project/kyma-environment-broker/common/orchestration" 15 "github.com/kyma-project/kyma-environment-broker/internal" 16 "github.com/kyma-project/kyma-environment-broker/internal/kubeconfig/automock" 17 "github.com/kyma-project/kyma-environment-broker/internal/logger" 18 "github.com/kyma-project/kyma-environment-broker/internal/storage" 19 20 "github.com/gorilla/mux" 21 "github.com/pivotal-cf/brokerapi/v8/domain" 22 "github.com/stretchr/testify/require" 23 ) 24 25 const ( 26 instanceID = "93241a34-8ab5-4f10-978e-eaa6f8ad551c" 27 operationID = "306f2406-e972-4fae-8edd-50fc50e56817" 28 instanceRuntimeID = "e04813ba-244a-4150-8670-506c37959388" 29 ) 30 31 func TestHandler_GetKubeconfig(t *testing.T) { 32 cases := map[string]struct { 33 pass bool 34 instanceID string 35 runtimeID string 36 operationStatus domain.LastOperationState 37 expectedStatusCode int 38 expectedErrorMessage string 39 }{ 40 "new kubeconfig was returned": { 41 pass: true, 42 instanceID: instanceID, 43 runtimeID: instanceRuntimeID, 44 expectedStatusCode: http.StatusOK, 45 }, 46 "instance ID is empty": { 47 pass: false, 48 instanceID: "", 49 expectedStatusCode: http.StatusNotFound, 50 expectedErrorMessage: "instanceID is required", 51 }, 52 "runtimeID not exist": { 53 pass: false, 54 instanceID: instanceID, 55 runtimeID: "", 56 expectedStatusCode: http.StatusNotFound, 57 expectedErrorMessage: fmt.Sprintf("kubeconfig for instance %s does not exist. Provisioning could be in progress, please try again later", instanceID), 58 }, 59 "provisioning operation is not ready": { 60 pass: false, 61 instanceID: instanceID, 62 runtimeID: instanceRuntimeID, 63 operationStatus: domain.InProgress, 64 expectedStatusCode: http.StatusNotFound, 65 expectedErrorMessage: fmt.Sprintf("provisioning operation for instance %s is in progress state, kubeconfig not exist yet, please try again later", instanceID), 66 }, 67 "unsuspension operation is not ready": { 68 pass: false, 69 instanceID: instanceID, 70 runtimeID: instanceRuntimeID, 71 operationStatus: orchestration.Pending, 72 expectedStatusCode: http.StatusNotFound, 73 expectedErrorMessage: fmt.Sprintf("provisioning operation for instance %s is in progress state, kubeconfig not exist yet, please try again later", instanceID), 74 }, 75 "provisioning operation failed": { 76 pass: false, 77 instanceID: instanceID, 78 runtimeID: instanceRuntimeID, 79 operationStatus: domain.Failed, 80 expectedStatusCode: http.StatusNotFound, 81 expectedErrorMessage: fmt.Sprintf("provisioning operation for instance %s failed, kubeconfig does not exist", instanceID), 82 }, 83 "kubeconfig builder failed": { 84 pass: false, 85 instanceID: instanceID, 86 runtimeID: instanceRuntimeID, 87 expectedStatusCode: http.StatusInternalServerError, 88 expectedErrorMessage: "cannot fetch SKR kubeconfig: builder error", 89 }, 90 } 91 for name, d := range cases { 92 t.Run(name, func(t *testing.T) { 93 // given 94 instance := internal.Instance{ 95 InstanceID: d.instanceID, 96 RuntimeID: d.runtimeID, 97 } 98 99 operation := internal.ProvisioningOperation{ 100 Operation: internal.Operation{ 101 ID: operationID, 102 InstanceID: instance.InstanceID, 103 State: d.operationStatus, 104 Type: internal.OperationTypeProvision, 105 }, 106 } 107 108 db := storage.NewMemoryStorage() 109 err := db.Instances().Insert(instance) 110 require.NoError(t, err) 111 err = db.Operations().InsertProvisioningOperation(operation) 112 require.NoError(t, err) 113 114 builder := &automock.KcBuilder{} 115 if d.pass { 116 builder.On("Build", &instance).Return("--kubeconfig file", nil) 117 defer builder.AssertExpectations(t) 118 } else { 119 builder.On("Build", &instance).Return("", fmt.Errorf("builder error")) 120 } 121 122 router := mux.NewRouter() 123 124 handler := NewHandler(db, builder, "", logger.NewLogDummy()) 125 handler.AttachRoutes(router) 126 127 server := httptest.NewServer(router) 128 129 // when 130 response, err := http.Get(fmt.Sprintf("%s/kubeconfig/%s", server.URL, d.instanceID)) 131 require.NoError(t, err) 132 133 // then 134 require.Equal(t, d.expectedStatusCode, response.StatusCode) 135 136 if d.pass { 137 require.Equal(t, "application/x-yaml", response.Header.Get("Content-Type")) 138 } else { 139 require.Equal(t, "application/json", response.Header.Get("Content-Type")) 140 } 141 142 body, err := ioutil.ReadAll(response.Body) 143 require.NoError(t, err) 144 145 if d.pass { 146 require.Equal(t, "--kubeconfig file", string(body)) 147 } else { 148 var errorResponse ErrorResponse 149 err := json.Unmarshal(body, &errorResponse) 150 require.NoError(t, err) 151 require.Equal(t, d.expectedErrorMessage, errorResponse.Error) 152 } 153 }) 154 } 155 } 156 157 func TestHandler_GetKubeconfigForOwnCluster(t *testing.T) { 158 // given 159 instance := internal.Instance{ 160 Parameters: internal.ProvisioningParameters{ 161 Parameters: internal.ProvisioningParametersDTO{ 162 Kubeconfig: "custom-kubeconfig", 163 }, 164 }, 165 InstanceDetails: internal.InstanceDetails{ 166 Kubeconfig: "custom-kubeconfig", 167 }, 168 InstanceID: instanceID, 169 RuntimeID: runtimeID, 170 ServicePlanID: broker.OwnClusterPlanID, 171 } 172 173 operation := internal.ProvisioningOperation{ 174 Operation: internal.Operation{ 175 ID: operationID, 176 InstanceID: instance.InstanceID, 177 State: domain.Succeeded, 178 InstanceDetails: internal.InstanceDetails{ 179 Kubeconfig: "custom-kubeconfig", 180 }, 181 Type: internal.OperationTypeProvision, 182 }, 183 } 184 185 db := storage.NewMemoryStorage() 186 err := db.Instances().Insert(instance) 187 require.NoError(t, err) 188 err = db.Operations().InsertProvisioningOperation(operation) 189 require.NoError(t, err) 190 191 // we do not expect usage of KcBuilder 192 builder := &automock.KcBuilder{} 193 defer builder.AssertExpectations(t) 194 195 router := mux.NewRouter() 196 197 handler := NewHandler(db, builder, "", logger.NewLogDummy()) 198 handler.AttachRoutes(router) 199 200 server := httptest.NewServer(router) 201 202 // when 203 response, err := http.Get(fmt.Sprintf("%s/kubeconfig/%s", server.URL, instanceID)) 204 require.NoError(t, err) 205 206 // then 207 assert.Equal(t, http.StatusNotFound, response.StatusCode) 208 } 209 210 func TestHandler_specifyAllowOriginHeader(t *testing.T) { 211 cases := map[string]struct { 212 requestHeader http.Header 213 origins string 214 corsHeaderExist bool 215 corsExpectedHeader string 216 }{ 217 "one origin which exist": { 218 requestHeader: map[string][]string{"Origin": {"https://example.com"}}, 219 origins: "https://example.com", 220 corsHeaderExist: true, 221 corsExpectedHeader: "https://example.com", 222 }, 223 "many origins which one exist": { 224 requestHeader: map[string][]string{"Origin": {"https://example.com"}}, 225 origins: "https://acme.com,https://example.com,https://eggplant.io", 226 corsHeaderExist: true, 227 corsExpectedHeader: "https://example.com", 228 }, 229 "many origins non one exist": { 230 requestHeader: map[string][]string{"Origin": {"https://example.com"}}, 231 origins: "https://acme.com,https://gopher.com,https://eggplant.io", 232 corsHeaderExist: false, 233 }, 234 "accept all origins": { 235 requestHeader: map[string][]string{"Origin": {"https://example.com"}}, 236 origins: "*", 237 corsHeaderExist: true, 238 corsExpectedHeader: "*", 239 }, 240 "no origin header in request": { 241 requestHeader: map[string][]string{}, 242 origins: "https://acme.com,https://example.com,https://eggplant.io", 243 corsHeaderExist: false, 244 }, 245 "wrong origin configuration": { 246 requestHeader: map[string][]string{"Origin": {"https://example.com"}}, 247 origins: "https://acme.com;https://example.com;https://eggplant.io", 248 corsHeaderExist: false, 249 }, 250 } 251 252 for name, d := range cases { 253 t.Run(name, func(t *testing.T) { 254 // given 255 request := &http.Request{Header: d.requestHeader} 256 response := &httptest.ResponseRecorder{} 257 258 handler := NewHandler(storage.NewMemoryStorage(), nil, d.origins, nil) 259 260 // when 261 handler.specifyAllowOriginHeader(request, response) 262 263 // then 264 if d.corsHeaderExist { 265 t.Log(response.Header()) 266 require.Equal(t, d.corsExpectedHeader, response.Header().Get("Access-Control-Allow-Origin")) 267 } else { 268 require.NotContains(t, "Access-Control-Allow-Origin", response.Header()) 269 } 270 }) 271 } 272 }