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  }