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 (
"encoding/json"
2019-11-28 19:31:37 +03:00
"fmt"
2019-10-16 06:29:17 +03:00
"net/http"
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"
"github.com/Azure/ARO-RP/pkg/database/cosmosdb"
"github.com/Azure/ARO-RP/pkg/frontend/middleware"
2019-10-16 06:29:17 +03:00
)
func ( f * frontend ) putOrPatchOpenShiftCluster ( w http . ResponseWriter , r * http . Request ) {
2019-12-09 19:44:41 +03:00
log := r . Context ( ) . Value ( middleware . ContextKeyLog ) . ( * logrus . Entry )
2019-10-16 06:29:17 +03:00
vars := mux . Vars ( r )
var b [ ] byte
2019-11-12 19:34:18 +03:00
var created bool
2019-12-09 19:44:41 +03:00
err := cosmosdb . RetryOnPreconditionFailed ( func ( ) error {
var err error
2019-12-03 00:57:05 +03:00
b , created , err = f . _putOrPatchOpenShiftCluster ( r , api . APIs [ vars [ "api-version" ] ] [ "OpenShiftCluster" ] . ( api . OpenShiftClusterToInternal ) , api . APIs [ vars [ "api-version" ] ] [ "OpenShiftCluster" ] . ( api . OpenShiftClusterToExternal ) )
2019-10-16 06:29:17 +03:00
return err
} )
2019-12-03 00:57:05 +03:00
if err == nil && created {
2019-11-12 19:34:18 +03:00
w . WriteHeader ( http . StatusCreated )
}
2019-12-03 00:57:05 +03:00
reply ( log , w , b , err )
2019-10-16 06:29:17 +03:00
}
2019-12-03 00:57:05 +03:00
func ( f * frontend ) _putOrPatchOpenShiftCluster ( r * http . Request , internal api . OpenShiftClusterToInternal , external api . OpenShiftClusterToExternal ) ( [ ] byte , bool , error ) {
2019-12-03 04:05:32 +03:00
vars := mux . Vars ( r )
2019-12-09 19:44:41 +03:00
body := r . Context ( ) . Value ( middleware . ContextKeyBody ) . ( [ ] byte )
2019-12-03 00:57:05 +03:00
2019-12-22 03:59:54 +03:00
subdoc , err := f . validateSubscriptionState ( r . URL . Path , api . SubscriptionStateRegistered )
2019-11-29 03:24:09 +03:00
if err != nil {
return nil , false , err
}
2019-12-22 03:59:54 +03:00
doc , err := f . db . OpenShiftClusters . Get ( r . URL . Path )
2019-10-18 22:01:46 +03:00
if err != nil && ! cosmosdb . IsErrorStatusCode ( err , http . StatusNotFound ) {
2019-11-12 19:34:18 +03:00
return nil , false , 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-11-12 19:34:18 +03:00
return nil , false , 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 ,
// TODO: ResourceGroup should be exposed in external API
ResourceGroup : vars [ "resourceName" ] ,
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 {
return nil , false , err
}
if doc . OpenShiftCluster . Properties . ProvisioningState == api . ProvisioningStateFailed {
switch doc . OpenShiftCluster . Properties . FailedProvisioningState {
case api . ProvisioningStateCreating :
return nil , false , api . NewCloudError ( http . StatusBadRequest , api . CloudErrorCodeRequestNotAllowed , "" , "Request is not allowed on cluster whose creation failed. Delete the cluster." )
case api . ProvisioningStateUpdating :
// allow
case api . ProvisioningStateDeleting :
return nil , false , api . NewCloudError ( http . StatusBadRequest , api . CloudErrorCodeRequestNotAllowed , "" , "Request is not allowed on cluster whose deletion failed. Delete the cluster." )
default :
return nil , false , 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 :
ext = external . OpenShiftClusterToExternal ( & api . OpenShiftCluster {
ID : doc . OpenShiftCluster . ID ,
Name : doc . OpenShiftCluster . Name ,
Type : doc . OpenShiftCluster . Type ,
Properties : api . Properties {
ProvisioningState : doc . OpenShiftCluster . Properties . ProvisioningState ,
} ,
} )
case http . MethodPatch :
ext = external . OpenShiftClusterToExternal ( doc . OpenShiftCluster )
}
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-11-12 19:34:18 +03:00
return nil , false , 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 {
err = internal . ValidateOpenShiftCluster ( f . env . Location ( ) , r . URL . Path , ext , nil )
} else {
err = internal . ValidateOpenShiftCluster ( f . env . Location ( ) , r . URL . Path , ext , doc . OpenShiftCluster )
}
2019-10-16 06:29:17 +03:00
if err != nil {
2019-11-12 19:34:18 +03:00
return nil , false , 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
2019-12-03 00:57:05 +03:00
internal . OpenShiftClusterToInternal ( 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 {
doc . OpenShiftCluster . Properties . ProvisioningState = api . ProvisioningStateCreating
} else {
doc . OpenShiftCluster . Properties . ProvisioningState = api . ProvisioningStateUpdating
doc . Dequeues = 0
}
2019-12-11 06:36:06 +03:00
err = internal . ValidateOpenShiftClusterDynamic ( r . Context ( ) , f . env . FPAuthorizer , doc . OpenShiftCluster )
2019-12-02 22:07:49 +03:00
if err != nil {
return nil , false , err
}
2019-11-29 22:36:00 +03:00
2019-12-02 22:07:49 +03:00
if isCreate {
doc , err = f . db . OpenShiftClusters . Create ( doc )
} else {
2019-11-28 00:39:22 +03:00
doc , err = f . db . OpenShiftClusters . Update ( doc )
2019-10-16 06:29:17 +03:00
}
if err != nil {
2019-11-12 19:34:18 +03:00
return nil , false , 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
2019-12-04 00:53:11 +03:00
b , err := json . MarshalIndent ( external . OpenShiftClusterToExternal ( doc . OpenShiftCluster ) , "" , " " )
2019-11-12 19:34:18 +03:00
if err != nil {
return nil , false , err
}
return b , isCreate , nil
2019-10-16 06:29:17 +03:00
}