2019-10-16 06:29:17 +03:00
package frontend
2019-12-17 04:16:50 +03:00
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
2019-10-16 06:29:17 +03:00
import (
2020-01-06 18:26:31 +03:00
"context"
2019-10-16 06:29:17 +03:00
"encoding/json"
2019-11-28 19:31:37 +03:00
"fmt"
2019-10-16 06:29:17 +03:00
"net/http"
2019-12-22 04:36:30 +03:00
"net/url"
2020-01-10 20:42:48 +03:00
"strings"
2019-10-16 06:29:17 +03:00
2019-11-28 00:18:49 +03:00
"github.com/Azure/go-autorest/autorest/azure"
2019-10-16 06:29:17 +03:00
"github.com/gorilla/mux"
uuid "github.com/satori/go.uuid"
"github.com/sirupsen/logrus"
2019-12-17 04:26:21 +03:00
"github.com/Azure/ARO-RP/pkg/api"
2020-02-06 15:23:35 +03:00
"github.com/Azure/ARO-RP/pkg/api/admin"
2019-12-17 04:26:21 +03:00
"github.com/Azure/ARO-RP/pkg/database/cosmosdb"
"github.com/Azure/ARO-RP/pkg/frontend/middleware"
2020-02-24 17:52:11 +03:00
"github.com/Azure/ARO-RP/pkg/util/version"
2019-10-16 06:29:17 +03:00
)
func ( f * frontend ) putOrPatchOpenShiftCluster ( w http . ResponseWriter , r * http . Request ) {
2020-01-06 18:26:31 +03:00
ctx := r . Context ( )
log := ctx . Value ( middleware . ContextKeyLog ) . ( * logrus . Entry )
2019-10-16 06:29:17 +03:00
vars := mux . Vars ( r )
2019-12-22 04:36:30 +03:00
var header http . Header
2019-10-16 06:29:17 +03:00
var b [ ] byte
2019-12-09 19:44:41 +03:00
err := cosmosdb . RetryOnPreconditionFailed ( func ( ) error {
var err error
2020-02-17 18:19:05 +03:00
b , err = f . _putOrPatchOpenShiftCluster ( ctx , r , & header , f . apis [ vars [ "api-version" ] ] . OpenShiftClusterConverter ( ) , f . apis [ vars [ "api-version" ] ] . OpenShiftClusterStaticValidator ( f . env . Location ( ) , r . URL . Path ) )
2019-10-16 06:29:17 +03:00
return err
} )
2019-12-03 00:57:05 +03:00
2019-12-22 04:36:30 +03:00
reply ( log , w , header , b , err )
2019-10-16 06:29:17 +03:00
}
2020-02-17 18:19:05 +03:00
func ( f * frontend ) _putOrPatchOpenShiftCluster ( ctx context . Context , r * http . Request , header * http . Header , converter api . OpenShiftClusterConverter , staticValidator api . OpenShiftClusterStaticValidator ) ( [ ] byte , error ) {
2019-12-09 19:44:41 +03:00
body := r . Context ( ) . Value ( middleware . ContextKeyBody ) . ( [ ] byte )
2019-12-03 00:57:05 +03:00
2020-01-06 18:26:31 +03:00
subdoc , err := f . validateSubscriptionState ( ctx , r . URL . Path , api . SubscriptionStateRegistered )
2019-11-29 03:24:09 +03:00
if err != nil {
2019-12-20 22:30:33 +03:00
return nil , err
2019-11-29 03:24:09 +03:00
}
2020-01-06 18:26:31 +03:00
doc , err := f . db . OpenShiftClusters . Get ( ctx , r . URL . Path )
2019-10-18 22:01:46 +03:00
if err != nil && ! cosmosdb . IsErrorStatusCode ( err , http . StatusNotFound ) {
2019-12-20 22:30:33 +03:00
return nil , err
2019-10-16 06:29:17 +03:00
}
isCreate := doc == nil
if isCreate {
2019-12-09 19:44:41 +03:00
originalPath := r . Context ( ) . Value ( middleware . ContextKeyOriginalPath ) . ( string )
2019-12-03 04:05:32 +03:00
originalR , err := azure . ParseResourceID ( originalPath )
2019-10-16 06:29:17 +03:00
if err != nil {
2019-12-20 22:30:33 +03:00
return nil , err
2019-10-16 06:29:17 +03:00
}
2019-12-03 04:05:32 +03:00
doc = & api . OpenShiftClusterDocument {
ID : uuid . NewV4 ( ) . String ( ) ,
2019-12-22 03:59:54 +03:00
Key : r . URL . Path ,
2019-12-03 04:05:32 +03:00
OpenShiftCluster : & api . OpenShiftCluster {
ID : originalPath ,
Name : originalR . ResourceName ,
2019-12-05 16:30:51 +03:00
Type : originalR . Provider + "/" + originalR . ResourceType ,
2019-10-16 06:29:17 +03:00
Properties : api . Properties {
2019-12-03 04:05:32 +03:00
ProvisioningState : api . ProvisioningStateSucceeded ,
2020-02-24 17:52:11 +03:00
ClusterProfile : api . ClusterProfile {
Version : version . OpenShiftVersion ,
} ,
2019-12-03 04:05:32 +03:00
ServicePrincipalProfile : api . ServicePrincipalProfile {
TenantID : subdoc . Subscription . Properties . TenantID ,
} ,
2019-10-16 06:29:17 +03:00
} ,
2019-12-03 04:05:32 +03:00
} ,
}
}
2019-10-16 06:29:17 +03:00
2019-12-03 04:05:32 +03:00
err = validateTerminalProvisioningState ( doc . OpenShiftCluster . Properties . ProvisioningState )
if err != nil {
2019-12-20 22:30:33 +03:00
return nil , err
2019-12-03 04:05:32 +03:00
}
if doc . OpenShiftCluster . Properties . ProvisioningState == api . ProvisioningStateFailed {
switch doc . OpenShiftCluster . Properties . FailedProvisioningState {
case api . ProvisioningStateCreating :
2019-12-20 22:30:33 +03:00
return nil , api . NewCloudError ( http . StatusBadRequest , api . CloudErrorCodeRequestNotAllowed , "" , "Request is not allowed on cluster whose creation failed. Delete the cluster." )
2019-12-03 04:05:32 +03:00
case api . ProvisioningStateUpdating :
2020-01-04 21:36:00 +03:00
doc . OpenShiftCluster . Properties . FailedProvisioningState = "" // allow
2019-12-03 04:05:32 +03:00
case api . ProvisioningStateDeleting :
2019-12-20 22:30:33 +03:00
return nil , api . NewCloudError ( http . StatusBadRequest , api . CloudErrorCodeRequestNotAllowed , "" , "Request is not allowed on cluster whose deletion failed. Delete the cluster." )
2019-12-03 04:05:32 +03:00
default :
2019-12-20 22:30:33 +03:00
return nil , fmt . Errorf ( "unexpected failedProvisioningState %q" , doc . OpenShiftCluster . Properties . FailedProvisioningState )
2019-10-16 06:29:17 +03:00
}
}
2019-12-03 04:05:32 +03:00
var ext interface { }
switch r . Method {
case http . MethodPut :
2020-01-04 21:09:47 +03:00
ext = converter . ToExternal ( & api . OpenShiftCluster {
2019-12-03 04:05:32 +03:00
ID : doc . OpenShiftCluster . ID ,
Name : doc . OpenShiftCluster . Name ,
Type : doc . OpenShiftCluster . Type ,
Properties : api . Properties {
ProvisioningState : doc . OpenShiftCluster . Properties . ProvisioningState ,
2020-02-24 17:52:11 +03:00
ClusterProfile : api . ClusterProfile {
Version : doc . OpenShiftCluster . Properties . ClusterProfile . Version ,
} ,
2019-12-03 04:05:32 +03:00
} ,
} )
case http . MethodPatch :
2020-01-04 21:09:47 +03:00
ext = converter . ToExternal ( doc . OpenShiftCluster )
2019-12-03 04:05:32 +03:00
}
2019-12-03 00:57:05 +03:00
err = json . Unmarshal ( body , & ext )
2019-10-16 06:29:17 +03:00
if err != nil {
2019-12-20 22:30:33 +03:00
return nil , api . NewCloudError ( http . StatusBadRequest , api . CloudErrorCodeInvalidRequestContent , "" , "The request content was invalid and could not be deserialized: %q." , err )
2019-10-16 06:29:17 +03:00
}
2019-12-03 04:05:32 +03:00
if isCreate {
2020-02-17 18:19:05 +03:00
err = staticValidator . Static ( ext , nil )
2019-12-03 04:05:32 +03:00
} else {
2020-02-17 18:19:05 +03:00
err = staticValidator . Static ( ext , doc . OpenShiftCluster )
2019-12-03 04:05:32 +03:00
}
2019-10-16 06:29:17 +03:00
if err != nil {
2019-12-20 22:30:33 +03:00
return nil , err
2019-10-16 06:29:17 +03:00
}
2019-11-28 00:18:49 +03:00
oldID , oldName , oldType := doc . OpenShiftCluster . ID , doc . OpenShiftCluster . Name , doc . OpenShiftCluster . Type
2020-01-04 21:09:47 +03:00
converter . ToInternal ( ext , doc . OpenShiftCluster )
2019-12-03 04:05:32 +03:00
doc . OpenShiftCluster . ID , doc . OpenShiftCluster . Name , doc . OpenShiftCluster . Type = oldID , oldName , oldType
2019-12-02 22:07:49 +03:00
2019-12-03 06:14:00 +03:00
if isCreate {
2020-01-10 20:42:48 +03:00
doc . ClusterResourceGroupIDKey = strings . ToLower ( doc . OpenShiftCluster . Properties . ClusterProfile . ResourceGroupID )
2020-02-17 22:41:15 +03:00
doc . ClientIDKey = strings . ToLower ( doc . OpenShiftCluster . Properties . ServicePrincipalProfile . ClientID )
2019-12-03 06:14:00 +03:00
doc . OpenShiftCluster . Properties . ProvisioningState = api . ProvisioningStateCreating
2020-01-18 19:33:08 +03:00
doc . Bucket , err = f . bucketAllocator . Allocate ( )
if err != nil {
return nil , err
}
2019-12-03 06:14:00 +03:00
} else {
2020-01-24 14:19:56 +03:00
// TODO: Get rid of the special case
vars := mux . Vars ( r )
2020-02-06 15:23:35 +03:00
if vars [ "api-version" ] == admin . APIVersion {
2020-01-24 14:19:56 +03:00
doc . OpenShiftCluster . Properties . ProvisioningState = api . ProvisioningStateAdminUpdating
} else {
doc . OpenShiftCluster . Properties . ProvisioningState = api . ProvisioningStateUpdating
}
2019-12-03 06:14:00 +03:00
doc . Dequeues = 0
}
2020-02-17 18:19:05 +03:00
err = f . ocDynamicValidator . Dynamic ( r . Context ( ) , doc . OpenShiftCluster )
2019-12-02 22:07:49 +03:00
if err != nil {
2019-12-20 22:30:33 +03:00
return nil , err
2019-12-02 22:07:49 +03:00
}
2019-11-29 22:36:00 +03:00
2020-01-06 18:26:31 +03:00
doc . AsyncOperationID , err = f . newAsyncOperation ( ctx , r , doc )
2019-12-22 04:36:30 +03:00
if err != nil {
return nil , err
}
2020-01-22 17:00:53 +03:00
u , err := url . Parse ( r . Header . Get ( "Referer" ) )
if err != nil {
return nil , err
}
2019-12-22 04:36:30 +03:00
2020-01-22 17:00:53 +03:00
u . Path = f . operationsPath ( r , doc . AsyncOperationID )
* header = http . Header {
"Azure-AsyncOperation" : [ ] string { u . String ( ) } ,
2019-12-22 04:36:30 +03:00
}
2019-12-02 22:07:49 +03:00
if isCreate {
2020-01-10 20:42:48 +03:00
newdoc , err := f . db . OpenShiftClusters . Create ( ctx , doc )
if cosmosdb . IsErrorStatusCode ( err , http . StatusPreconditionFailed ) {
2020-02-17 22:41:15 +03:00
return nil , f . validateOpenShiftUniqueKey ( ctx , doc )
2020-01-10 20:42:48 +03:00
}
doc = newdoc
2019-12-02 22:07:49 +03:00
} else {
2020-01-06 18:26:31 +03:00
doc , err = f . db . OpenShiftClusters . Update ( ctx , doc )
2019-10-16 06:29:17 +03:00
}
if err != nil {
2019-12-20 22:30:33 +03:00
return nil , err
2019-10-16 06:29:17 +03:00
}
2019-11-18 06:07:44 +03:00
doc . OpenShiftCluster . Properties . ServicePrincipalProfile . ClientSecret = ""
2019-10-16 06:29:17 +03:00
2020-01-04 21:09:47 +03:00
b , err := json . MarshalIndent ( converter . ToExternal ( doc . OpenShiftCluster ) , "" , " " )
2019-11-12 19:34:18 +03:00
if err != nil {
2019-12-20 22:30:33 +03:00
return nil , err
2019-11-12 19:34:18 +03:00
}
2019-12-20 22:30:33 +03:00
if isCreate {
err = statusCodeError ( http . StatusCreated )
}
return b , err
2019-10-16 06:29:17 +03:00
}