mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 18:54:06 +00:00
Merge pull request #50706 from freehan/cloud-provider-op-v2
Automatic merge from submit-queue (batch tested with PRs 50967, 50505, 50706, 51033, 51028) teach gce cloud to handle alpha/beta operations v2 Alternative to #50704 This one feels cleaner. BUT, type assertion problems cannot be exposed at compile time. Please let me know what you think. This will set the precedence for consuming GCE alpha/beta API. cc: @thockin @yujuhong @saad-ali @MrHohn ```release-note NONE ```
This commit is contained in:
commit
e2685d800d
@ -89,6 +89,8 @@ go_test(
|
||||
"//pkg/kubelet/apis:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/golang.org/x/oauth2/google:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v0.alpha:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v0.beta:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/googleapi:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
|
@ -80,6 +80,11 @@ const (
|
||||
gceComputeAPIEndpoint = "https://www.googleapis.com/compute/v1/"
|
||||
)
|
||||
|
||||
// gceObject is an abstraction of all GCE API object in go client
|
||||
type gceObject interface {
|
||||
MarshalJSON() ([]byte, error)
|
||||
}
|
||||
|
||||
// GCECloud is an implementation of Interface, LoadBalancer and Instances for Google Compute Engine.
|
||||
type GCECloud struct {
|
||||
// ClusterID contains functionality for getting (and initializing) the ingress-uid. Call GCECloud.Initialize()
|
||||
|
@ -17,17 +17,20 @@ limitations under the License.
|
||||
package gce
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"github.com/golang/glog"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
computev1 "google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
func (gce *GCECloud) waitForOp(op *compute.Operation, getOperation func(operationName string) (*compute.Operation, error), mc *metricContext) error {
|
||||
func (gce *GCECloud) waitForOp(op *computev1.Operation, getOperation func(operationName string) (*computev1.Operation, error), mc *metricContext) error {
|
||||
if op == nil {
|
||||
return mc.Observe(fmt.Errorf("operation must not be nil"))
|
||||
}
|
||||
@ -72,11 +75,11 @@ func (gce *GCECloud) waitForOp(op *compute.Operation, getOperation func(operatio
|
||||
})
|
||||
}
|
||||
|
||||
func opIsDone(op *compute.Operation) bool {
|
||||
func opIsDone(op *computev1.Operation) bool {
|
||||
return op != nil && op.Status == "DONE"
|
||||
}
|
||||
|
||||
func getErrorFromOp(op *compute.Operation) error {
|
||||
func getErrorFromOp(op *computev1.Operation) error {
|
||||
if op != nil && op.Error != nil && len(op.Error.Errors) > 0 {
|
||||
err := &googleapi.Error{
|
||||
Code: int(op.HttpErrorStatusCode),
|
||||
@ -89,20 +92,77 @@ func getErrorFromOp(op *compute.Operation) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gce *GCECloud) waitForGlobalOp(op *compute.Operation, mc *metricContext) error {
|
||||
return gce.waitForOp(op, func(operationName string) (*compute.Operation, error) {
|
||||
return gce.service.GlobalOperations.Get(gce.projectID, operationName).Do()
|
||||
}, mc)
|
||||
func (gce *GCECloud) waitForGlobalOp(op gceObject, mc *metricContext) error {
|
||||
switch v := op.(type) {
|
||||
case *computealpha.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceAlpha.GlobalOperations.Get(gce.projectID, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computebeta.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceBeta.GlobalOperations.Get(gce.projectID, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computev1.Operation:
|
||||
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
|
||||
return gce.service.GlobalOperations.Get(gce.projectID, operationName).Do()
|
||||
}, mc)
|
||||
default:
|
||||
return fmt.Errorf("unexpected type: %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (gce *GCECloud) waitForRegionOp(op *compute.Operation, region string, mc *metricContext) error {
|
||||
return gce.waitForOp(op, func(operationName string) (*compute.Operation, error) {
|
||||
return gce.service.RegionOperations.Get(gce.projectID, region, operationName).Do()
|
||||
}, mc)
|
||||
func (gce *GCECloud) waitForRegionOp(op gceObject, region string, mc *metricContext) error {
|
||||
switch v := op.(type) {
|
||||
case *computealpha.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceAlpha.RegionOperations.Get(gce.projectID, region, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computebeta.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceBeta.RegionOperations.Get(gce.projectID, region, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computev1.Operation:
|
||||
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
|
||||
return gce.service.RegionOperations.Get(gce.projectID, region, operationName).Do()
|
||||
}, mc)
|
||||
default:
|
||||
return fmt.Errorf("unexpected type: %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (gce *GCECloud) waitForZoneOp(op *compute.Operation, zone string, mc *metricContext) error {
|
||||
return gce.waitForOp(op, func(operationName string) (*compute.Operation, error) {
|
||||
return gce.service.ZoneOperations.Get(gce.projectID, zone, operationName).Do()
|
||||
}, mc)
|
||||
func (gce *GCECloud) waitForZoneOp(op gceObject, zone string, mc *metricContext) error {
|
||||
switch v := op.(type) {
|
||||
case *computealpha.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceAlpha.ZoneOperations.Get(gce.projectID, zone, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computebeta.Operation:
|
||||
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
|
||||
op, err := gce.serviceBeta.ZoneOperations.Get(gce.projectID, zone, operationName).Do()
|
||||
return convertToV1Operation(op), err
|
||||
}, mc)
|
||||
case *computev1.Operation:
|
||||
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
|
||||
return gce.service.ZoneOperations.Get(gce.projectID, zone, operationName).Do()
|
||||
}, mc)
|
||||
default:
|
||||
return fmt.Errorf("unexpected type: %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
func convertToV1Operation(object gceObject) *computev1.Operation {
|
||||
enc, err := object.MarshalJSON()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to encode to json: %v", err))
|
||||
}
|
||||
var op computev1.Operation
|
||||
if err := json.Unmarshal(enc, &op); err != nil {
|
||||
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to v1 operation: %v", object, err))
|
||||
}
|
||||
return &op
|
||||
}
|
||||
|
@ -17,10 +17,15 @@ limitations under the License.
|
||||
package gce
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"golang.org/x/oauth2/google"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
computealpha "google.golang.org/api/compute/v0.alpha"
|
||||
computebeta "google.golang.org/api/compute/v0.beta"
|
||||
computev1 "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func TestExtraKeyInConfig(t *testing.T) {
|
||||
@ -437,3 +442,53 @@ func TestGenerateCloudConfigs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToV1Operation(t *testing.T) {
|
||||
v1Op := getTestOperation()
|
||||
enc, _ := v1Op.MarshalJSON()
|
||||
var op interface{}
|
||||
var alphaOp computealpha.Operation
|
||||
var betaOp computebeta.Operation
|
||||
|
||||
if err := json.Unmarshal(enc, &alphaOp); err != nil {
|
||||
t.Errorf("Failed to unmarshal operation: %v", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(enc, &betaOp); err != nil {
|
||||
t.Errorf("Failed to unmarshal operation: %v", err)
|
||||
}
|
||||
|
||||
op = convertToV1Operation(&alphaOp)
|
||||
if _, ok := op.(*computev1.Operation); ok {
|
||||
if !reflect.DeepEqual(op, v1Op) {
|
||||
t.Errorf("Failed to maintain consistency across conversion")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Expect output to be type v1 operation, but got %v", op)
|
||||
}
|
||||
|
||||
op = convertToV1Operation(&betaOp)
|
||||
if _, ok := op.(*computev1.Operation); ok {
|
||||
if !reflect.DeepEqual(op, v1Op) {
|
||||
t.Errorf("Failed to maintain consistency across conversion")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Expect output to be type v1 operation, but got %v", op)
|
||||
}
|
||||
}
|
||||
|
||||
func getTestOperation() *computev1.Operation {
|
||||
return &computev1.Operation{
|
||||
Name: "test",
|
||||
Description: "test",
|
||||
Id: uint64(12345),
|
||||
Error: &computev1.OperationError{
|
||||
Errors: []*computev1.OperationErrorErrors{
|
||||
{
|
||||
Code: "555",
|
||||
Message: "error",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user