Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a 'Typename' strongtype for representing all resource types #3588

Merged
merged 1 commit into from
Jan 23, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkg/api/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

"github.com/davecgh/go-spew/spew"
)
Expand Down Expand Up @@ -60,3 +61,9 @@ var Semantic = conversion.EqualitiesOrDie(
return a.Amount.Cmp(b.Amount) == 0
},
)

var standardResources = util.NewStringSet(string(ResourceMemory), string(ResourceCPU))

func IsStandardResourceName(str string) bool {
return standardResources.Has(str)
}
18 changes: 18 additions & 0 deletions pkg/api/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,21 @@ func TestSemantic(t *testing.T) {
}
}
}

func TestIsStandardResource(t *testing.T) {
testCases := []struct {
input string
output bool
}{
{"cpu", true},
{"memory", true},
{"disk", false},
{"blah", false},
{"x.y.z", false},
}
for i, tc := range testCases {
if IsStandardResourceName(tc.input) != tc.output {
t.Errorf("case[%d], expected: %t, got: %t", i, tc.output, !tc.output)
}
}
}
2 changes: 2 additions & 0 deletions pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,8 @@ type NodeResources struct {
type ResourceName string

const (
// The default compute resource namespace for all standard resource types.
DefaultResourceNamespace = "kubernetes.io"
// CPU, in cores. (500m = .5 cores)
ResourceCPU ResourceName = "cpu"
// Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/v1beta3/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,8 @@ type NodeCondition struct {
type ResourceName string

const (
// The default compute resource namespace for all standard resource types.
DefaultResourceNamespace = "kubernetes.io"
// CPU, in cores. (500m = .5 cores)
ResourceCPU ResourceName = "cpu"
// Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)
Expand Down
27 changes: 27 additions & 0 deletions pkg/api/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,3 +622,30 @@ func ValidateMinionUpdate(oldMinion *api.Node, minion *api.Node) errs.Validation
}
return allErrs
}

// Typename is a generic representation for all compute resource typenames.
// Refer to docs/resources.md for more details.
func ValidateResourceName(str string) errs.ValidationErrorList {
if !util.IsQualifiedName(str) {
return errs.ValidationErrorList{fmt.Errorf("invalid compute resource typename format %q", str)}
}

parts := strings.Split(str, "/")
switch len(parts) {
case 1:
if !api.IsStandardResourceName(parts[0]) {
return errs.ValidationErrorList{fmt.Errorf("invalid compute resource typename. %q is neither a standard resource type nor is fully qualified", str)}
}
break
case 2:
if parts[0] == api.DefaultResourceNamespace {
if !api.IsStandardResourceName(parts[1]) {
return errs.ValidationErrorList{fmt.Errorf("invalid compute resource typename. %q contains a compute resource type not supported", str)}

}
}
break
}

return errs.ValidationErrorList{}
}
37 changes: 37 additions & 0 deletions pkg/api/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1440,3 +1440,40 @@ func TestValidateMinionUpdate(t *testing.T) {
}
}
}

func TestValidateResourceNames(t *testing.T) {
longString := "a"
for i := 0; i < 6; i++ {
longString += longString
}
table := []struct {
input string
success bool
}{
{"memory", true},
{"cpu", true},
{"network", false},
{"disk", false},
{"", false},
{".", false},
{"..", false},
{"kubernetes.io/cpu", true},
{"kubernetes.io/disk", false},
{"my.favorite.app.co/12345", true},
{"my.favorite.app.co/_12345", false},
{"my.favorite.app.co/12345_", false},
{"kubernetes.io/..", false},
{"kubernetes.io/" + longString, false},
{"kubernetes.io//", false},
{"kubernetes.io", false},
{"kubernetes.io/will/not/work/", false},
}
for _, item := range table {
err := ValidateResourceName(item.input)
if len(err) != 0 && item.success {
t.Errorf("expected no failure for input %q", item.input)
} else if len(err) == 0 && !item.success {
t.Errorf("expected failure for input %q", item.input)
}
}
}
19 changes: 19 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"path"
"reflect"
"regexp"
"runtime"
"strconv"
"strings"
"time"

"github.com/golang/glog"
Expand Down Expand Up @@ -183,3 +185,20 @@ func AllPtrFieldsNil(obj interface{}) bool {
}
return true
}

// Splits a fully qualified name and returns its namespace and name.
// Assumes that the input 'str' has been validated.
func SplitQualifiedName(str string) (string, string) {
parts := strings.Split(str, "/")
if len(parts) < 2 {
return "", str
}

return parts[0], parts[1]
}

// Joins 'namespace' and 'name' and returns a fully qualified name
// Assumes that the input is valid.
func JoinQualifiedName(namespace, name string) string {
return path.Join(namespace, name)
}
34 changes: 34 additions & 0 deletions pkg/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,37 @@ func TestAllPtrFieldsNil(t *testing.T) {
}
}
}

func TestSplitQualifiedName(t *testing.T) {
testCases := []struct {
input string
output []string
}{
{"kubernetes.io/blah", []string{"kubernetes.io", "blah"}},
{"blah", []string{"", "blah"}},
{"kubernetes.io/blah/blah", []string{"kubernetes.io", "blah"}},
}
for i, tc := range testCases {
namespace, name := SplitQualifiedName(tc.input)
if namespace != tc.output[0] || name != tc.output[1] {
t.Errorf("case[%d]: expected (%q, %q), got (%q, %q)", i, tc.output[0], tc.output[1], namespace, name)
}
}
}

func TestJoinQualifiedName(t *testing.T) {
testCases := []struct {
input []string
output string
}{
{[]string{"kubernetes.io", "blah"}, "kubernetes.io/blah"},
{[]string{"blah", ""}, "blah"},
{[]string{"kubernetes.io", "blah"}, "kubernetes.io/blah"},
}
for i, tc := range testCases {
res := JoinQualifiedName(tc.input[0], tc.input[1])
if res != tc.output {
t.Errorf("case[%d]: expected %q, got %q", i, tc.output, res)
}
}
}