mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-02 16:29:21 +00:00
Support utilities
This commit is contained in:
parent
78c8249cb3
commit
6894e3d32b
167
pkg/cloudprovider/providers/gce/cloud/utils.go
Normal file
167
pkg/cloudprovider/providers/gce/cloud/utils.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
gaPrefix = "https://www.googleapis.com/compute/v1/"
|
||||||
|
alphaPrefix = "https://www.googleapis.com/compute/alpha/"
|
||||||
|
betaPrefix = "https://www.googleapis.com/compute/beta/"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
allPrefixes = []string{gaPrefix, alphaPrefix, betaPrefix}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceID identifies a GCE resource as parsed from compute resource URL.
|
||||||
|
type ResourceID struct {
|
||||||
|
ProjectID string
|
||||||
|
Resource string
|
||||||
|
Key *meta.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if two resource IDs are equal.
|
||||||
|
func (r *ResourceID) Equal(other *ResourceID) bool {
|
||||||
|
if r.ProjectID != other.ProjectID || r.Resource != other.Resource {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if r.Key != nil && other.Key != nil {
|
||||||
|
return *r.Key == *other.Key
|
||||||
|
}
|
||||||
|
if r.Key == nil && other.Key == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseResourceURL parses resource URLs of the following formats:
|
||||||
|
//
|
||||||
|
// projects/<proj>/global/<res>/<name>
|
||||||
|
// projects/<proj>/regions/<region>/<res>/<name>
|
||||||
|
// projects/<proj>/zones/<zone>/<res>/<name>
|
||||||
|
// [https://www.googleapis.com/compute/<ver>]/projects/<proj>/global/<res>/<name>
|
||||||
|
// [https://www.googleapis.com/compute/<ver>]/projects/<proj>/regions/<region>/<res>/<name>
|
||||||
|
// [https://www.googleapis.com/compute/<ver>]/projects/<proj>/zones/<zone>/<res>/<name>
|
||||||
|
func ParseResourceURL(url string) (*ResourceID, error) {
|
||||||
|
errNotValid := fmt.Errorf("%q is not a valid resource URL", url)
|
||||||
|
|
||||||
|
// Remove the "https://..." prefix if present
|
||||||
|
for _, prefix := range allPrefixes {
|
||||||
|
if strings.HasPrefix(url, prefix) {
|
||||||
|
if len(url) < len(prefix) {
|
||||||
|
return nil, errNotValid
|
||||||
|
}
|
||||||
|
url = url[len(prefix):]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(url, "/")
|
||||||
|
if len(parts) < 2 || parts[0] != "projects" {
|
||||||
|
return nil, errNotValid
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := &ResourceID{ProjectID: parts[1]}
|
||||||
|
if len(parts) == 2 {
|
||||||
|
ret.Resource = "projects"
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parts) < 4 {
|
||||||
|
return nil, errNotValid
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parts) == 4 {
|
||||||
|
switch parts[2] {
|
||||||
|
case "regions":
|
||||||
|
ret.Resource = "regions"
|
||||||
|
ret.Key = meta.GlobalKey(parts[3])
|
||||||
|
return ret, nil
|
||||||
|
case "zones":
|
||||||
|
ret.Resource = "zones"
|
||||||
|
ret.Key = meta.GlobalKey(parts[3])
|
||||||
|
return ret, nil
|
||||||
|
default:
|
||||||
|
return nil, errNotValid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch parts[2] {
|
||||||
|
case "global":
|
||||||
|
if len(parts) != 5 {
|
||||||
|
return nil, errNotValid
|
||||||
|
}
|
||||||
|
ret.Resource = parts[3]
|
||||||
|
ret.Key = meta.GlobalKey(parts[4])
|
||||||
|
return ret, nil
|
||||||
|
case "regions":
|
||||||
|
if len(parts) != 6 {
|
||||||
|
return nil, errNotValid
|
||||||
|
}
|
||||||
|
ret.Resource = parts[4]
|
||||||
|
ret.Key = meta.RegionalKey(parts[5], parts[3])
|
||||||
|
return ret, nil
|
||||||
|
case "zones":
|
||||||
|
if len(parts) != 6 {
|
||||||
|
return nil, errNotValid
|
||||||
|
}
|
||||||
|
ret.Resource = parts[4]
|
||||||
|
ret.Key = meta.ZonalKey(parts[5], parts[3])
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
return nil, errNotValid
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyViaJSON(dest, src interface{}) error {
|
||||||
|
bytes, err := json.Marshal(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.Unmarshal(bytes, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelfLink returns the self link URL for the given object.
|
||||||
|
func SelfLink(ver meta.Version, project, resource string, key meta.Key) string {
|
||||||
|
var prefix string
|
||||||
|
switch ver {
|
||||||
|
case meta.VersionAlpha:
|
||||||
|
prefix = alphaPrefix
|
||||||
|
case meta.VersionBeta:
|
||||||
|
prefix = betaPrefix
|
||||||
|
case meta.VersionGA:
|
||||||
|
prefix = gaPrefix
|
||||||
|
default:
|
||||||
|
prefix = "invalid-prefix"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch key.Type() {
|
||||||
|
case meta.Zonal:
|
||||||
|
return fmt.Sprintf("%sprojects/%s/zones/%s/%s/%s", prefix, project, key.Zone, resource, key.Name)
|
||||||
|
case meta.Regional:
|
||||||
|
return fmt.Sprintf("%sprojects/%s/regions/%s/%s/%s", prefix, project, key.Region, resource, key.Name)
|
||||||
|
case meta.Global:
|
||||||
|
return fmt.Sprintf("%sprojects/%s/%s/%s", prefix, project, resource, key.Name)
|
||||||
|
}
|
||||||
|
return "invalid-self-link"
|
||||||
|
}
|
197
pkg/cloudprovider/providers/gce/cloud/utils_test.go
Normal file
197
pkg/cloudprovider/providers/gce/cloud/utils_test.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseResourceURL(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
in string
|
||||||
|
r *ResourceID
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"https://www.googleapis.com/compute/v1/projects/some-gce-project",
|
||||||
|
&ResourceID{"some-gce-project", "projects", nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"https://www.googleapis.com/compute/v1/projects/some-gce-project/regions/us-central1",
|
||||||
|
&ResourceID{"some-gce-project", "regions", meta.GlobalKey("us-central1")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"https://www.googleapis.com/compute/v1/projects/some-gce-project/zones/us-central1-b",
|
||||||
|
&ResourceID{"some-gce-project", "zones", meta.GlobalKey("us-central1-b")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"https://www.googleapis.com/compute/v1/projects/some-gce-project/global/operations/operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf",
|
||||||
|
&ResourceID{"some-gce-project", "operations", meta.GlobalKey("operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"https://www.googleapis.com/compute/alpha/projects/some-gce-project/regions/us-central1/addresses/my-address",
|
||||||
|
&ResourceID{"some-gce-project", "addresses", meta.RegionalKey("my-address", "us-central1")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"https://www.googleapis.com/compute/v1/projects/some-gce-project/zones/us-central1-c/instances/instance-1",
|
||||||
|
&ResourceID{"some-gce-project", "instances", meta.ZonalKey("instance-1", "us-central1-c")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projects/some-gce-project",
|
||||||
|
&ResourceID{"some-gce-project", "projects", nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projects/some-gce-project/regions/us-central1",
|
||||||
|
&ResourceID{"some-gce-project", "regions", meta.GlobalKey("us-central1")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projects/some-gce-project/zones/us-central1-b",
|
||||||
|
&ResourceID{"some-gce-project", "zones", meta.GlobalKey("us-central1-b")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projects/some-gce-project/global/operations/operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf",
|
||||||
|
&ResourceID{"some-gce-project", "operations", meta.GlobalKey("operation-1513289952196-56054460af5a0-b1dae0c3-9bbf9dbf")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projects/some-gce-project/regions/us-central1/addresses/my-address",
|
||||||
|
&ResourceID{"some-gce-project", "addresses", meta.RegionalKey("my-address", "us-central1")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projects/some-gce-project/zones/us-central1-c/instances/instance-1",
|
||||||
|
&ResourceID{"some-gce-project", "instances", meta.ZonalKey("instance-1", "us-central1-c")},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
r, err := ParseResourceURL(tc.in)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ParseResourceURL(%q) = %+v, %v; want _, nil", tc.in, r, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !r.Equal(tc.r) {
|
||||||
|
t.Errorf("ParseResourceURL(%q) = %+v, nil; want %+v, nil", tc.in, r, tc.r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Malformed URLs.
|
||||||
|
for _, tc := range []string{
|
||||||
|
"",
|
||||||
|
"/",
|
||||||
|
"/a",
|
||||||
|
"/a/b",
|
||||||
|
"/a/b/c",
|
||||||
|
"/a/b/c/d",
|
||||||
|
"/a/b/c/d/e",
|
||||||
|
"/a/b/c/d/e/f",
|
||||||
|
"https://www.googleapis.com/compute/v1/projects/some-gce-project/global",
|
||||||
|
"projects/some-gce-project/global",
|
||||||
|
"projects/some-gce-project/global/foo/bar/baz",
|
||||||
|
"projects/some-gce-project/zones/us-central1-c/res",
|
||||||
|
"projects/some-gce-project/zones/us-central1-c/res/name/extra",
|
||||||
|
"https://www.googleapis.com/compute/gamma/projects/some-gce-project/global/addresses/name",
|
||||||
|
} {
|
||||||
|
r, err := ParseResourceURL(tc)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("ParseResourceURL(%q) = %+v, %v, want _, error", tc, r, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
A, B, C string
|
||||||
|
}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
A, B, D string
|
||||||
|
}
|
||||||
|
|
||||||
|
type E struct{}
|
||||||
|
|
||||||
|
func (*E) MarshalJSON() ([]byte, error) {
|
||||||
|
return nil, errors.New("injected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyVisJSON(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var b B
|
||||||
|
srcA := &A{"aa", "bb", "cc"}
|
||||||
|
err := copyViaJSON(&b, srcA)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(`copyViaJSON(&b, %+v) = %v, want nil`, srcA, err)
|
||||||
|
} else {
|
||||||
|
expectedB := B{"aa", "bb", ""}
|
||||||
|
if b != expectedB {
|
||||||
|
t.Errorf("b == %+v, want %+v", b, expectedB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var a A
|
||||||
|
srcB := &B{"aaa", "bbb", "ccc"}
|
||||||
|
err = copyViaJSON(&a, srcB)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(`copyViaJSON(&a, %+v) = %v, want nil`, srcB, err)
|
||||||
|
} else {
|
||||||
|
expectedA := A{"aaa", "bbb", ""}
|
||||||
|
if a != expectedA {
|
||||||
|
t.Errorf("a == %+v, want %+v", a, expectedA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := copyViaJSON(&a, &E{}); err == nil {
|
||||||
|
t.Errorf("copyViaJSON(&a, &E{}) = nil, want error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelfLink(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for _, tc := range []struct{
|
||||||
|
ver meta.Version
|
||||||
|
project string
|
||||||
|
resource string
|
||||||
|
key meta.Key
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
meta.VersionAlpha,
|
||||||
|
"proj1",
|
||||||
|
"addresses",
|
||||||
|
*meta.RegionalKey("key1", "us-central1"),
|
||||||
|
"https://www.googleapis.com/compute/alpha/projects/proj1/regions/us-central1/addresses/key1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
meta.VersionBeta,
|
||||||
|
"proj3",
|
||||||
|
"disks",
|
||||||
|
*meta.ZonalKey("key2", "us-central1-b"),
|
||||||
|
"https://www.googleapis.com/compute/beta/projects/proj3/zones/us-central1-b/disks/key2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
meta.VersionGA,
|
||||||
|
"proj4",
|
||||||
|
"urlMaps",
|
||||||
|
*meta.GlobalKey("key3"),
|
||||||
|
"https://www.googleapis.com/compute/v1/projects/proj4/urlMaps/key3",
|
||||||
|
},
|
||||||
|
}{
|
||||||
|
if link := SelfLink(tc.ver, tc.project, tc.resource, tc.key); link != tc.want {
|
||||||
|
t.Errorf("SelfLink(%v, %q, %q, %v) = %v, want %q", tc.ver, tc.project, tc.resource, tc.key, link, tc.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user