Skip to content

Commit

Permalink
Add Template Api types and wiring
Browse files Browse the repository at this point in the history
Feature Issue: kubernetes/enhancements#3

- Add Template types to extensions/types.go and extensions/v1beta1/types.go
- Add etcd storage for Template object
- Add stragegy for Template object
- Register Template Api types in the extensions api
- Register Template Api CRUD and Process endpoints in the master
- Add generated client expansions
- Add kubectl 'get' and 'describe' support for Template Api objects
  • Loading branch information
pwittrock committed May 17, 2016
1 parent 58c7e41 commit c7a56db
Show file tree
Hide file tree
Showing 18 changed files with 758 additions and 9 deletions.
31 changes: 22 additions & 9 deletions pkg/api/validation/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,37 +94,44 @@ func (s *SwaggerSchema) validateItems(items interface{}) []error {
if !ok {
return append(allErrs, fmt.Errorf("items isn't a slice"))
}
for i, item := range itemList {
return s.validateObjectSlice("items", itemList)
}

// validateObjectSlice validates a field on an Object that is a collection of Generic objects.
// Each object in slice has its metadata fields checked and is then passed to ValidateObject
func (s *SwaggerSchema) validateObjectSlice(fieldName string, slice []interface{}) []error {
allErrs := []error{}
for i, item := range slice {
fields, ok := item.(map[string]interface{})
if !ok {
allErrs = append(allErrs, fmt.Errorf("items[%d] isn't a map[string]interface{}", i))
allErrs = append(allErrs, fmt.Errorf("%s[%d] isn't a map[string]interface{}", fieldName, i))
continue
}
groupVersion := fields["apiVersion"]
if groupVersion == nil {
allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion not set", i))
allErrs = append(allErrs, fmt.Errorf("%s[%d].apiVersion not set", fieldName, i))
continue
}
itemVersion, ok := groupVersion.(string)
if !ok {
allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion isn't string type", i))
allErrs = append(allErrs, fmt.Errorf("%s[%d].apiVersion isn't string type", fieldName, i))
continue
}
if len(itemVersion) == 0 {
allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion is empty", i))
allErrs = append(allErrs, fmt.Errorf("%s[%d].apiVersion is empty", fieldName, i))
}
kind := fields["kind"]
if kind == nil {
allErrs = append(allErrs, fmt.Errorf("items[%d].kind not set", i))
allErrs = append(allErrs, fmt.Errorf("%s[%d].kind not set", fieldName, i))
continue
}
itemKind, ok := kind.(string)
if !ok {
allErrs = append(allErrs, fmt.Errorf("items[%d].kind isn't string type", i))
allErrs = append(allErrs, fmt.Errorf("%s[%d].kind isn't string type", fieldName, i))
continue
}
if len(itemKind) == 0 {
allErrs = append(allErrs, fmt.Errorf("items[%d].kind is empty", i))
allErrs = append(allErrs, fmt.Errorf("%s[%d].kind is empty", fieldName, i))
}
version := apiutil.GetVersion(itemVersion)
errs := s.ValidateObject(item, "", version+"."+itemKind)
Expand Down Expand Up @@ -226,7 +233,13 @@ func (s *SwaggerSchema) ValidateObject(obj interface{}, fieldName, typeName stri
// This is because the actual values will be of some sub-type (e.g. Deployment) not the expected
// super-type (RawExtention)
if s.isGenericArray(details) {
errs := s.validateItems(value)
allErrs := []error{}
genericSlice, ok := value.([]interface{})
if !ok {
allErrs = append(allErrs, fmt.Errorf("genericSlice isn't a slice"))
continue
}
errs := s.validateObjectSlice(key, genericSlice)
if len(errs) > 0 {
allErrs = append(allErrs, errs...)
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/apis/extensions/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,16 @@ func addKnownTypes(scheme *runtime.Scheme) {
&ThirdPartyResourceDataList{},
&Ingress{},
&IngressList{},
&api.List{},
&api.ListOptions{},
&ReplicaSet{},
&ReplicaSetList{},
&api.ExportOptions{},
&PodSecurityPolicy{},
&PodSecurityPolicyList{},
&Template{},
&TemplateList{},
&TemplateParameters{},
)
}

Expand All @@ -93,3 +97,6 @@ func (obj *ReplicaSet) GetObjectKind() unversioned.ObjectKind {
func (obj *ReplicaSetList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *PodSecurityPolicy) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *PodSecurityPolicyList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *Template) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *TemplateList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *TemplateParameters) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
78 changes: 78 additions & 0 deletions pkg/apis/extensions/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/intstr"
)

Expand Down Expand Up @@ -809,3 +810,80 @@ type PodSecurityPolicyList struct {

Items []PodSecurityPolicy `json:"items"`
}

// +genclient=true

// Template defines a list of partially complete Objects and a list of Parameters that
// can be processed into a Config by substituting parameterized values at processing time.
// Parameter values maybe set at processing time.
type Template struct {
unversioned.TypeMeta `json:",inline"`
api.ObjectMeta `json:"metadata,omitempty"`

Spec TemplateSpec `json:"spec"`
}

type TemplateSpec struct {
// Parameters is a list of Parameters used to substitute values into Template Objects
Parameters []Parameter `json:"parameters,omitempty"`

// Objects is a list of partially complete Objects with substitution symbols.
Objects []runtime.RawExtension `json:"objects"`
}

// TemplateList is a list of Template objects.
type TemplateList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
Items []Template `json:"items"`
}

const (
StringParam = "string"
IntParam = "integer"
BoolParam = "boolean"
Base64Param = "base64"
)

// Parameter defines a name/value variable that is substituted into Template Objects at
// Template processing time.
type Parameter struct {
// Name defines the symbol to be replaced. Name should appear as $(Name) in Objects.
Name string `json:"name"`

// DisplayName is used instead of Name when displaying the Parameter in a UI
DisplayName string `json:"displayName,omitempty"`

// Description is used to give context about the Parameter, such as its purpose
// and what values are acceptable.
Description string `json:"description,omitempty"`

// Value holds a default value to replace all occurrences of $(Name) within the Template's
// Objects when the Template is processed. The Value maybe overridden at Template
// processing time. If no Value is provided as a default or override, then
// the empty string will be used for substitution.
Value string `json:"value,omitempty"`

// Required indicates the parameter must have a non-empty value when the Template is processed.
// Parameters of Type 'integer' and 'boolean' are always Required.
Required bool `json:"required,omitempty"`

// Type is the type that the parameter value must be parsed to. Type may be one of
// 'string', 'integer', 'boolean', or 'base64'.
// Type is used by clients to provide validation of user input and direction to users.
// Parameters used to define integer or boolean fields (e.g. replicaCount) should have the
// Type set to integer or boolean accordingly.
Type string `json:"type,omitempty"`
}

// TemplateParameters contains the substitution parameter overrides when processing a Template
type TemplateParameters struct {
unversioned.TypeMeta `json:",inline"`

// Name is the name of the Template to be processed.
Name string `json:"name"`

// ParameterValues is a map of substitution parameter name:value pairs to be expanded in the Template.
// Values defined in this map will override any defaults already set in the Template.
ParameterValues map[string]string `json:"parameters,omitempty"`
}
31 changes: 31 additions & 0 deletions pkg/apis/extensions/v1beta1/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func addDefaultingFuncs(scheme *runtime.Scheme) {
SetDefaults_Job,
SetDefaults_HorizontalPodAutoscaler,
SetDefaults_ReplicaSet,
SetDefaults_Parameter,
SetDefaults_TemplateParameters,
)
}

Expand Down Expand Up @@ -150,3 +152,32 @@ func SetDefaults_ReplicaSet(obj *ReplicaSet) {
*obj.Spec.Replicas = 1
}
}

func SetDefaults_Parameter(obj *Parameter) {
if obj.Required == nil {
obj.Required = new(bool)
*obj.Required = false
}
if obj.Type == nil {
obj.Type = new(string)
*obj.Type = StringParam
}
if obj.Value == nil {
obj.Value = new(string)
*obj.Value = ""
}
if obj.DisplayName == nil {
obj.DisplayName = new(string)
*obj.DisplayName = ""
}
if obj.Description == nil {
obj.Description = new(string)
*obj.Description = ""
}
}

func SetDefaults_TemplateParameters(obj *TemplateParameters) {
if obj.ParameterValues == nil {
obj.ParameterValues = map[string]string{}
}
}
7 changes: 7 additions & 0 deletions pkg/apis/extensions/v1beta1/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,16 @@ func addKnownTypes(scheme *runtime.Scheme) {
&ThirdPartyResourceDataList{},
&Ingress{},
&IngressList{},
&v1.List{},
&ListOptions{},
&v1.DeleteOptions{},
&ReplicaSet{},
&ReplicaSetList{},
&PodSecurityPolicy{},
&PodSecurityPolicyList{},
&Template{},
&TemplateList{},
&TemplateParameters{},
)
// Add the watch version that applies
versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion)
Expand Down Expand Up @@ -88,3 +92,6 @@ func (obj *ReplicaSet) GetObjectKind() unversioned.ObjectKind {
func (obj *ReplicaSetList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *PodSecurityPolicy) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *PodSecurityPolicyList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *Template) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *TemplateList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *TemplateParameters) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
78 changes: 78 additions & 0 deletions pkg/apis/extensions/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/intstr"
)

Expand Down Expand Up @@ -1102,3 +1103,80 @@ type PodSecurityPolicyList struct {
// Items is a list of schema objects.
Items []PodSecurityPolicy `json:"items" protobuf:"bytes,2,rep,name=items"`
}

// +genclient=true

// Template defines a list of partially complete Objects and a list of Parameters that
// can be processed into a Config by substituting parameterized values at processing time.
// Parameter values maybe set at processing time.
type Template struct {
unversioned.TypeMeta `json:",inline"`
v1.ObjectMeta `json:"metadata,omitempty"`

Spec TemplateSpec `json:"spec"`
}

type TemplateSpec struct {
// Parameters is a list of Parameters used to substitute values into Template Objects
Parameters []Parameter `json:"parameters,omitempty"`

// Objects is a list of partially complete Objects with substitution symbols.
Objects []runtime.RawExtension `json:"objects"`
}

// TemplateList is a list of Template objects.
type TemplateList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
Items []Template `json:"items,omitempty"`
}

const (
StringParam = "string"
IntParam = "integer"
BoolParam = "boolean"
Base64Param = "base64"
)

// Parameter defines a name/value variable that is substituted into Template Objects at
// Template processing time.
type Parameter struct {
// Name defines the symbol to be replaced. Name should appear as $(Name) in Objects.
Name string `json:"name"`

// DisplayName is used instead of Name when displaying the Parameter in a UI
DisplayName *string `json:"displayName,omitempty"`

// Description is used to give context about the Parameter, such as its purpose
// and what values are acceptable.
Description *string `json:"description,omitempty"`

// Value holds a default value to replace all occurrences of $(Name) within the Template's
// Objects when the Template is processed. The Value maybe overridden at Template
// processing time. If no Value is provided as a default or override, then
// the empty string will be used for substitution.
Value *string `json:"value,omitempty"`

// Required indicates the parameter must have a non-empty value when the Template is processed.
// Parameters of Type 'integer' and 'boolean' are always Required.
Required *bool `json:"required,omitempty"`

// Type is the type that the parameter value must be parsed to. Type may be one of
// 'string', 'integer', 'boolean', or 'base64'.
// Type is used by clients to provide validation of user input and direction to users.
// Parameters used to define integer or boolean fields (e.g. replicaCount) should have the
// Type set to integer or boolean accordingly.
Type *string `json:"type,omitempty"`
}

// TemplateParameters contains the substitution parameter overrides when processing a Template
type TemplateParameters struct {
unversioned.TypeMeta `json:",inline"`

// Name is the name of the Template to be processed.
Name string `json:"name"`

// ParameterValues is a map of substitution parameter name:value pairs to be expanded in the Template.
// Values defined in this map will override any defaults already set in the Template.
ParameterValues map[string]string `json:"parameters,omitempty"`
}
24 changes: 24 additions & 0 deletions pkg/apis/extensions/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ func ValidateDeploymentName(name string, prefix bool) (bool, string) {
return apivalidation.NameIsDNSSubdomain(name, prefix)
}

// Validates that the given name can be used as a Template name.
func ValidateTemplateName(name string, prefix bool) (bool, string) {
return apivalidation.NameIsDNSSubdomain(name, prefix)
}

func ValidatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if intOrPercent.Type == intstr.String {
Expand Down Expand Up @@ -687,3 +692,22 @@ func ValidatePodSecurityPolicyUpdate(old *extensions.PodSecurityPolicy, new *ext
allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&new.Spec, field.NewPath("spec"))...)
return allErrs
}

// Template Api Validation
func ValidateTemplateSpec(spec *extensions.TemplateSpec, fldPath *field.Path) field.ErrorList {
// TODO: Write this
allErrs := field.ErrorList{}
return allErrs
}

func ValidateTemplateUpdate(update, old *extensions.Template) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateTemplateSpec(&update.Spec, field.NewPath("spec"))...)
return allErrs
}

func ValidateTemplate(obj *extensions.Template) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateTemplateName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateTemplateSpec(&obj.Spec, field.NewPath("spec"))...)
return allErrs
}
Loading

0 comments on commit c7a56db

Please sign in to comment.