mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
move daemon controller to the experimental api
This commit is contained in:
parent
650bf71cf7
commit
8460e3913d
@ -83,7 +83,6 @@ var standardResources = util.NewStringSet(
|
||||
string(ResourceQuotas),
|
||||
string(ResourceServices),
|
||||
string(ResourceReplicationControllers),
|
||||
string(ResourceDaemon),
|
||||
string(ResourceSecrets),
|
||||
string(ResourcePersistentVolumeClaims),
|
||||
string(ResourceStorage))
|
||||
|
@ -95,7 +95,6 @@ func init() {
|
||||
"PodExecOptions",
|
||||
"PodAttachOptions",
|
||||
"PodProxyOptions",
|
||||
"Daemon",
|
||||
"ThirdPartyResource",
|
||||
"ThirdPartyResourceData",
|
||||
"ThirdPartyResourceList")
|
||||
|
@ -32,8 +32,6 @@ func init() {
|
||||
&PodTemplateList{},
|
||||
&ReplicationControllerList{},
|
||||
&ReplicationController{},
|
||||
&DaemonList{},
|
||||
&Daemon{},
|
||||
&ServiceList{},
|
||||
&Service{},
|
||||
&NodeList{},
|
||||
@ -85,8 +83,6 @@ func (*PodTemplate) IsAnAPIObject() {}
|
||||
func (*PodTemplateList) IsAnAPIObject() {}
|
||||
func (*ReplicationController) IsAnAPIObject() {}
|
||||
func (*ReplicationControllerList) IsAnAPIObject() {}
|
||||
func (*Daemon) IsAnAPIObject() {}
|
||||
func (*DaemonList) IsAnAPIObject() {}
|
||||
func (*Service) IsAnAPIObject() {}
|
||||
func (*ServiceList) IsAnAPIObject() {}
|
||||
func (*Endpoints) IsAnAPIObject() {}
|
||||
|
@ -120,7 +120,7 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
|
||||
c.FuzzNoCustom(j) // fuzz self without calling this function again
|
||||
//j.TemplateRef = nil // this is required for round trip
|
||||
},
|
||||
func(j *api.DaemonSpec, c fuzz.Continue) {
|
||||
func(j *expapi.DaemonSpec, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(j) // fuzz self without calling this function again
|
||||
},
|
||||
func(j *api.List, c fuzz.Continue) {
|
||||
|
@ -1058,54 +1058,6 @@ type ReplicationControllerList struct {
|
||||
Items []ReplicationController `json:"items"`
|
||||
}
|
||||
|
||||
// DaemonSpec is the specification of a daemon.
|
||||
type DaemonSpec struct {
|
||||
// Selector is a label query over pods that are managed by the daemon.
|
||||
Selector map[string]string `json:"selector"`
|
||||
|
||||
// Template is the object that describes the pod that will be created.
|
||||
// The Daemon will create exactly one copy of this pod on every node
|
||||
// that matches the template's node selector (or on every node if no node
|
||||
// selector is specified).
|
||||
Template *PodTemplateSpec `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
// DaemonStatus represents the current status of a daemon.
|
||||
type DaemonStatus struct {
|
||||
// CurrentNumberScheduled is the number of nodes that are running exactly 1 copy of the
|
||||
// daemon and are supposed to run the daemon.
|
||||
CurrentNumberScheduled int `json:"currentNumberScheduled"`
|
||||
|
||||
// NumberMisscheduled is the number of nodes that are running the daemon, but are
|
||||
// not supposed to run the daemon.
|
||||
NumberMisscheduled int `json:"numberMisscheduled"`
|
||||
|
||||
// DesiredNumberScheduled is the total number of nodes that should be running the daemon
|
||||
// (including nodes correctly running the daemon).
|
||||
DesiredNumberScheduled int `json:"desiredNumberScheduled"`
|
||||
}
|
||||
|
||||
// Daemon represents the configuration of a daemon.
|
||||
type Daemon struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the desired behavior of this daemon.
|
||||
Spec DaemonSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status is the current status of this daemon. This data may be
|
||||
// out of date by some window of time.
|
||||
Status DaemonStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// DaemonList is a collection of daemon.
|
||||
type DaemonList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []Daemon `json:"items"`
|
||||
}
|
||||
|
||||
const (
|
||||
// ClusterIPNone - do not assign a cluster IP
|
||||
// no proxying required and no environment variables should be created for pods
|
||||
@ -1978,8 +1930,6 @@ const (
|
||||
ResourceServices ResourceName = "services"
|
||||
// ReplicationControllers, number
|
||||
ResourceReplicationControllers ResourceName = "replicationcontrollers"
|
||||
// Daemon, number
|
||||
ResourceDaemon ResourceName = "daemon"
|
||||
// ResourceQuotas, number
|
||||
ResourceQuotas ResourceName = "resourcequotas"
|
||||
// ResourceSecrets, number
|
||||
|
@ -44,21 +44,6 @@ func addDefaultingFuncs() {
|
||||
*obj.Spec.Replicas = 1
|
||||
}
|
||||
},
|
||||
func(obj *Daemon) {
|
||||
var labels map[string]string
|
||||
if obj.Spec.Template != nil {
|
||||
labels = obj.Spec.Template.Labels
|
||||
}
|
||||
// TODO: support templates defined elsewhere when we support them in the API
|
||||
if labels != nil {
|
||||
if len(obj.Spec.Selector) == 0 {
|
||||
obj.Spec.Selector = labels
|
||||
}
|
||||
if len(obj.Labels) == 0 {
|
||||
obj.Labels = labels
|
||||
}
|
||||
}
|
||||
},
|
||||
func(obj *Volume) {
|
||||
if util.AllPtrFieldsNil(&obj.VolumeSource) {
|
||||
obj.VolumeSource = VolumeSource{
|
||||
|
@ -155,64 +155,6 @@ func TestSetDefaultReplicationController(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultDaemon(t *testing.T) {
|
||||
tests := []struct {
|
||||
dc *versioned.Daemon
|
||||
expectLabelsChange bool
|
||||
}{
|
||||
{
|
||||
dc: &versioned.Daemon{
|
||||
Spec: versioned.DaemonSpec{
|
||||
Template: &versioned.PodTemplateSpec{
|
||||
ObjectMeta: versioned.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectLabelsChange: true,
|
||||
},
|
||||
{
|
||||
dc: &versioned.Daemon{
|
||||
ObjectMeta: versioned.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"bar": "foo",
|
||||
},
|
||||
},
|
||||
Spec: versioned.DaemonSpec{
|
||||
Template: &versioned.PodTemplateSpec{
|
||||
ObjectMeta: versioned.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectLabelsChange: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
dc := test.dc
|
||||
obj2 := roundTrip(t, runtime.Object(dc))
|
||||
dc2, ok := obj2.(*versioned.Daemon)
|
||||
if !ok {
|
||||
t.Errorf("unexpected object: %v", dc2)
|
||||
t.FailNow()
|
||||
}
|
||||
if test.expectLabelsChange != reflect.DeepEqual(dc2.Labels, dc2.Spec.Template.Labels) {
|
||||
if test.expectLabelsChange {
|
||||
t.Errorf("expected: %v, got: %v", dc2.Spec.Template.Labels, dc2.Labels)
|
||||
} else {
|
||||
t.Errorf("unexpected equality: %v", dc.Labels)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newInt(val int) *int {
|
||||
p := new(int)
|
||||
*p = val
|
||||
|
@ -47,8 +47,6 @@ func addKnownTypes() {
|
||||
&PodTemplateList{},
|
||||
&ReplicationController{},
|
||||
&ReplicationControllerList{},
|
||||
&DaemonList{},
|
||||
&Daemon{},
|
||||
&Service{},
|
||||
&ServiceList{},
|
||||
&Endpoints{},
|
||||
@ -100,8 +98,6 @@ func (*PodTemplate) IsAnAPIObject() {}
|
||||
func (*PodTemplateList) IsAnAPIObject() {}
|
||||
func (*ReplicationController) IsAnAPIObject() {}
|
||||
func (*ReplicationControllerList) IsAnAPIObject() {}
|
||||
func (*Daemon) IsAnAPIObject() {}
|
||||
func (*DaemonList) IsAnAPIObject() {}
|
||||
func (*Service) IsAnAPIObject() {}
|
||||
func (*ServiceList) IsAnAPIObject() {}
|
||||
func (*Endpoints) IsAnAPIObject() {}
|
||||
|
@ -1365,67 +1365,6 @@ type ReplicationControllerList struct {
|
||||
Items []ReplicationController `json:"items"`
|
||||
}
|
||||
|
||||
// DaemonSpec is the specification of a daemon.
|
||||
type DaemonSpec struct {
|
||||
// Selector is a label query over pods that are managed by the daemon.
|
||||
// Must match in order to be controlled.
|
||||
// If empty, defaulted to labels on Pod template.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors
|
||||
Selector map[string]string `json:"selector,omitempty"`
|
||||
|
||||
// Template is the object that describes the pod that will be created.
|
||||
// The Daemon will create exactly one copy of this pod on every node
|
||||
// that matches the template's node selector (or on every node if no node
|
||||
// selector is specified).
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template
|
||||
Template *PodTemplateSpec `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
// DaemonStatus represents the current status of a daemon.
|
||||
type DaemonStatus struct {
|
||||
// CurrentNumberScheduled is the number of nodes that are running exactly 1 copy of the
|
||||
// daemon and are supposed to run the daemon.
|
||||
CurrentNumberScheduled int `json:"currentNumberScheduled"`
|
||||
|
||||
// NumberMisscheduled is the number of nodes that are running the daemon, but are
|
||||
// not supposed to run the daemon.
|
||||
NumberMisscheduled int `json:"numberMisscheduled"`
|
||||
|
||||
// DesiredNumberScheduled is the total number of nodes that should be running the daemon
|
||||
// (including nodes correctly running the daemon).
|
||||
DesiredNumberScheduled int `json:"desiredNumberScheduled"`
|
||||
}
|
||||
|
||||
// Daemon represents the configuration of a daemon.
|
||||
type Daemon struct {
|
||||
TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
|
||||
ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the specification of the desired behavior of this daemon.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
|
||||
Spec DaemonSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status is the current status of this daemon. This data may be
|
||||
// out of date by some window of time.
|
||||
// Populated by the system.
|
||||
// Read-only.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
|
||||
Status DaemonStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// DaemonList is a collection of daemon.
|
||||
type DaemonList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
// Standard list metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
|
||||
ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Items is a list of daemons.
|
||||
Items []Daemon `json:"items"`
|
||||
}
|
||||
|
||||
// Session Affinity Type string
|
||||
type ServiceAffinity string
|
||||
|
||||
@ -2370,8 +2309,6 @@ const (
|
||||
ResourceServices ResourceName = "services"
|
||||
// ReplicationControllers, number
|
||||
ResourceReplicationControllers ResourceName = "replicationcontrollers"
|
||||
// Daemon, number
|
||||
ResourceDaemon ResourceName = "daemon"
|
||||
// ResourceQuotas, number
|
||||
ResourceQuotas ResourceName = "resourcequotas"
|
||||
// ResourceSecrets, number
|
||||
|
@ -112,13 +112,6 @@ func ValidateReplicationControllerName(name string, prefix bool) (bool, string)
|
||||
return NameIsDNSSubdomain(name, prefix)
|
||||
}
|
||||
|
||||
// ValidateDaemonName can be used to check whether the given daemon name is valid.
|
||||
// Prefix indicates this name will be used as part of generation, in which case
|
||||
// trailing dashes are allowed.
|
||||
func ValidateDaemonName(name string, prefix bool) (bool, string) {
|
||||
return NameIsDNSSubdomain(name, prefix)
|
||||
}
|
||||
|
||||
// ValidateServiceName can be used to check whether the given service name is valid.
|
||||
// Prefix indicates this name will be used as part of generation, in which case
|
||||
// trailing dashes are allowed.
|
||||
@ -1244,65 +1237,6 @@ func ValidateReplicationControllerSpec(spec *api.ReplicationControllerSpec) errs
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemon tests if required fields in the daemon are set.
|
||||
func ValidateDaemon(controller *api.Daemon) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, ValidateObjectMeta(&controller.ObjectMeta, true, ValidateReplicationControllerName).Prefix("metadata")...)
|
||||
allErrs = append(allErrs, ValidateDaemonSpec(&controller.Spec).Prefix("spec")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemonUpdate tests if required fields in the daemon are set.
|
||||
func ValidateDaemonUpdate(oldController, controller *api.Daemon) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta).Prefix("metadata")...)
|
||||
allErrs = append(allErrs, ValidateDaemonSpec(&controller.Spec).Prefix("spec")...)
|
||||
allErrs = append(allErrs, ValidateDaemonTemplateUpdate(oldController.Spec.Template, controller.Spec.Template).Prefix("spec.template")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemonTemplateUpdate tests that certain fields in the daemon's pod template are not updated.
|
||||
func ValidateDaemonTemplateUpdate(oldPodTemplate, podTemplate *api.PodTemplateSpec) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
podSpec := podTemplate.Spec
|
||||
// podTemplate.Spec is not a pointer, so we can modify NodeSelector and NodeName directly.
|
||||
podSpec.NodeSelector = oldPodTemplate.Spec.NodeSelector
|
||||
podSpec.NodeName = oldPodTemplate.Spec.NodeName
|
||||
// In particular, we do not allow updates to container images at this point.
|
||||
if !api.Semantic.DeepEqual(oldPodTemplate.Spec, podSpec) {
|
||||
// TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("spec", "content of spec is not printed out, please refer to the \"details\"", "may not update fields other than spec.nodeSelector"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemonSpec tests if required fields in the daemon spec are set.
|
||||
func ValidateDaemonSpec(spec *api.DaemonSpec) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
selector := labels.Set(spec.Selector).AsSelector()
|
||||
if selector.Empty() {
|
||||
allErrs = append(allErrs, errs.NewFieldRequired("selector"))
|
||||
}
|
||||
|
||||
if spec.Template == nil {
|
||||
allErrs = append(allErrs, errs.NewFieldRequired("template"))
|
||||
} else {
|
||||
labels := labels.Set(spec.Template.Labels)
|
||||
if !selector.Matches(labels) {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("template.metadata.labels", spec.Template.Labels, "selector does not match template"))
|
||||
}
|
||||
allErrs = append(allErrs, ValidatePodTemplateSpec(spec.Template).Prefix("template")...)
|
||||
// Daemons typically run on more than one node, so mark Read-Write persistent disks as invalid.
|
||||
allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(spec.Template.Spec.Volumes).Prefix("template.spec.volumes")...)
|
||||
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
|
||||
if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways {
|
||||
allErrs = append(allErrs, errs.NewFieldValueNotSupported("template.spec.restartPolicy", spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePodTemplateSpec validates the spec of a pod template
|
||||
func ValidatePodTemplateSpec(spec *api.PodTemplateSpec) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
|
@ -2365,418 +2365,6 @@ func TestValidateReplicationController(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDaemonUpdate(t *testing.T) {
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validSelector2 := map[string]string{"c": "d"}
|
||||
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||
|
||||
validPodSpecAbc := api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
}
|
||||
validPodSpecDef := api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "def", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
}
|
||||
validPodSpecNodeSelector := api.PodSpec{
|
||||
NodeSelector: validSelector,
|
||||
NodeName: "xyz",
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
}
|
||||
validPodSpecVolume := api.PodSpec{
|
||||
Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
}
|
||||
|
||||
validPodTemplateAbc := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: validPodSpecAbc,
|
||||
},
|
||||
}
|
||||
validPodTemplateNodeSelector := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: validPodSpecNodeSelector,
|
||||
},
|
||||
}
|
||||
validPodTemplateAbc2 := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector2,
|
||||
},
|
||||
Spec: validPodSpecAbc,
|
||||
},
|
||||
}
|
||||
validPodTemplateDef := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector2,
|
||||
},
|
||||
Spec: validPodSpecDef,
|
||||
},
|
||||
}
|
||||
invalidPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: invalidSelector,
|
||||
},
|
||||
},
|
||||
}
|
||||
readWriteVolumePodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: validPodSpecVolume,
|
||||
},
|
||||
}
|
||||
|
||||
type dcUpdateTest struct {
|
||||
old api.Daemon
|
||||
update api.Daemon
|
||||
}
|
||||
successCases := []dcUpdateTest{
|
||||
{
|
||||
old: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
old: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector2,
|
||||
Template: &validPodTemplateAbc2.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
old: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateNodeSelector.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
successCase.old.ObjectMeta.ResourceVersion = "1"
|
||||
successCase.update.ObjectMeta.ResourceVersion = "1"
|
||||
if errs := ValidateDaemonUpdate(&successCase.old, &successCase.update); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
errorCases := map[string]dcUpdateTest{
|
||||
"change daemon name": {
|
||||
old: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid selector": {
|
||||
old: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: invalidSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid pod": {
|
||||
old: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &invalidPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"change container image": {
|
||||
old: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateDef.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"read-write volume": {
|
||||
old: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: api.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &readWriteVolumePodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for testName, errorCase := range errorCases {
|
||||
if errs := ValidateDaemonUpdate(&errorCase.old, &errorCase.update); len(errs) == 0 {
|
||||
t.Errorf("expected failure: %s", testName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDaemon(t *testing.T) {
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||
invalidPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: invalidSelector,
|
||||
},
|
||||
},
|
||||
}
|
||||
successCases := []api.Daemon{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
if errs := ValidateDaemon(&successCase); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
errorCases := map[string]api.Daemon{
|
||||
"zero-length ID": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"missing-namespace": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc-123"},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"empty selector": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"selector_doesnt_match": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid manifest": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
},
|
||||
},
|
||||
"invalid_label": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Labels: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid_label 2": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Labels: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: api.DaemonSpec{
|
||||
Template: &invalidPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid_annotation": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Annotations: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid restart policy 1": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid restart policy 2": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: api.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyNever,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
errs := ValidateDaemon(&v)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected failure for %s", k)
|
||||
}
|
||||
for i := range errs {
|
||||
field := errs[i].(*errors.ValidationError).Field
|
||||
if !strings.HasPrefix(field, "spec.template.") &&
|
||||
field != "metadata.name" &&
|
||||
field != "metadata.namespace" &&
|
||||
field != "spec.selector" &&
|
||||
field != "spec.template" &&
|
||||
field != "GCEPersistentDisk.ReadOnly" &&
|
||||
field != "spec.template.labels" &&
|
||||
field != "metadata.annotations" &&
|
||||
field != "metadata.labels" {
|
||||
t.Errorf("%s: missing prefix for: %v", k, errs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateNode(t *testing.T) {
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||
|
13
pkg/client/unversioned/cache/listers.go
vendored
13
pkg/client/unversioned/cache/listers.go
vendored
@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
@ -231,7 +232,7 @@ type StoreToDaemonLister struct {
|
||||
}
|
||||
|
||||
// Exists checks if the given dc exists in the store.
|
||||
func (s *StoreToDaemonLister) Exists(daemon *api.Daemon) (bool, error) {
|
||||
func (s *StoreToDaemonLister) Exists(daemon *expapi.Daemon) (bool, error) {
|
||||
_, exists, err := s.Store.Get(daemon)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@ -241,17 +242,17 @@ func (s *StoreToDaemonLister) Exists(daemon *api.Daemon) (bool, error) {
|
||||
|
||||
// StoreToDaemonLister lists all daemons in the store.
|
||||
// TODO: converge on the interface in pkg/client
|
||||
func (s *StoreToDaemonLister) List() (daemons []api.Daemon, err error) {
|
||||
func (s *StoreToDaemonLister) List() (daemons []expapi.Daemon, err error) {
|
||||
for _, c := range s.Store.List() {
|
||||
daemons = append(daemons, *(c.(*api.Daemon)))
|
||||
daemons = append(daemons, *(c.(*expapi.Daemon)))
|
||||
}
|
||||
return daemons, nil
|
||||
}
|
||||
|
||||
// GetPodDaemons returns a list of daemons managing a pod. Returns an error iff no matching daemons are found.
|
||||
func (s *StoreToDaemonLister) GetPodDaemons(pod *api.Pod) (daemons []api.Daemon, err error) {
|
||||
func (s *StoreToDaemonLister) GetPodDaemons(pod *api.Pod) (daemons []expapi.Daemon, err error) {
|
||||
var selector labels.Selector
|
||||
var daemonController api.Daemon
|
||||
var daemonController expapi.Daemon
|
||||
|
||||
if len(pod.Labels) == 0 {
|
||||
err = fmt.Errorf("No daemons found for pod %v because it has no labels", pod.Name)
|
||||
@ -259,7 +260,7 @@ func (s *StoreToDaemonLister) GetPodDaemons(pod *api.Pod) (daemons []api.Daemon,
|
||||
}
|
||||
|
||||
for _, m := range s.Store.List() {
|
||||
daemonController = *m.(*api.Daemon)
|
||||
daemonController = *m.(*expapi.Daemon)
|
||||
if daemonController.Namespace != pod.Namespace {
|
||||
continue
|
||||
}
|
||||
|
31
pkg/client/unversioned/cache/listers_test.go
vendored
31
pkg/client/unversioned/cache/listers_test.go
vendored
@ -20,6 +20,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
@ -159,44 +160,44 @@ func TestStoreToDaemonLister(t *testing.T) {
|
||||
store := NewStore(MetaNamespaceKeyFunc)
|
||||
lister := StoreToDaemonLister{store}
|
||||
testCases := []struct {
|
||||
inDCs []*api.Daemon
|
||||
list func() ([]api.Daemon, error)
|
||||
inDCs []*expapi.Daemon
|
||||
list func() ([]expapi.Daemon, error)
|
||||
outDCNames util.StringSet
|
||||
expectErr bool
|
||||
}{
|
||||
// Basic listing
|
||||
{
|
||||
inDCs: []*api.Daemon{
|
||||
inDCs: []*expapi.Daemon{
|
||||
{ObjectMeta: api.ObjectMeta{Name: "basic"}},
|
||||
},
|
||||
list: func() ([]api.Daemon, error) {
|
||||
list: func() ([]expapi.Daemon, error) {
|
||||
return lister.List()
|
||||
},
|
||||
outDCNames: util.NewStringSet("basic"),
|
||||
},
|
||||
// Listing multiple controllers
|
||||
{
|
||||
inDCs: []*api.Daemon{
|
||||
inDCs: []*expapi.Daemon{
|
||||
{ObjectMeta: api.ObjectMeta{Name: "basic"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "complex"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "complex2"}},
|
||||
},
|
||||
list: func() ([]api.Daemon, error) {
|
||||
list: func() ([]expapi.Daemon, error) {
|
||||
return lister.List()
|
||||
},
|
||||
outDCNames: util.NewStringSet("basic", "complex", "complex2"),
|
||||
},
|
||||
// No pod labels
|
||||
{
|
||||
inDCs: []*api.Daemon{
|
||||
inDCs: []*expapi.Daemon{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"},
|
||||
Spec: api.DaemonSpec{
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: map[string]string{"foo": "baz"},
|
||||
},
|
||||
},
|
||||
},
|
||||
list: func() ([]api.Daemon, error) {
|
||||
list: func() ([]expapi.Daemon, error) {
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "pod1", Namespace: "ns"},
|
||||
}
|
||||
@ -207,12 +208,12 @@ func TestStoreToDaemonLister(t *testing.T) {
|
||||
},
|
||||
// No RC selectors
|
||||
{
|
||||
inDCs: []*api.Daemon{
|
||||
inDCs: []*expapi.Daemon{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"},
|
||||
},
|
||||
},
|
||||
list: func() ([]api.Daemon, error) {
|
||||
list: func() ([]expapi.Daemon, error) {
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod1",
|
||||
@ -227,21 +228,21 @@ func TestStoreToDaemonLister(t *testing.T) {
|
||||
},
|
||||
// Matching labels to selectors and namespace
|
||||
{
|
||||
inDCs: []*api.Daemon{
|
||||
inDCs: []*expapi.Daemon{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.DaemonSpec{
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "ns"},
|
||||
Spec: api.DaemonSpec{
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
list: func() ([]api.Daemon, error) {
|
||||
list: func() ([]expapi.Daemon, error) {
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod1",
|
||||
|
@ -33,7 +33,6 @@ type Interface interface {
|
||||
PodsNamespacer
|
||||
PodTemplatesNamespacer
|
||||
ReplicationControllersNamespacer
|
||||
DaemonsNamespacer
|
||||
ServicesNamespacer
|
||||
EndpointsNamespacer
|
||||
VersionInterface
|
||||
@ -53,10 +52,6 @@ func (c *Client) ReplicationControllers(namespace string) ReplicationControllerI
|
||||
return newReplicationControllers(c, namespace)
|
||||
}
|
||||
|
||||
func (c *Client) Daemons(namespace string) DaemonInterface {
|
||||
return newDaemons(c, namespace)
|
||||
}
|
||||
|
||||
func (c *Client) Nodes() NodeInterface {
|
||||
return newNodes(c)
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||
package unversioned
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
@ -29,50 +29,50 @@ type DaemonsNamespacer interface {
|
||||
}
|
||||
|
||||
type DaemonInterface interface {
|
||||
List(selector labels.Selector) (*api.DaemonList, error)
|
||||
Get(name string) (*api.Daemon, error)
|
||||
Create(ctrl *api.Daemon) (*api.Daemon, error)
|
||||
Update(ctrl *api.Daemon) (*api.Daemon, error)
|
||||
List(selector labels.Selector) (*expapi.DaemonList, error)
|
||||
Get(name string) (*expapi.Daemon, error)
|
||||
Create(ctrl *expapi.Daemon) (*expapi.Daemon, error)
|
||||
Update(ctrl *expapi.Daemon) (*expapi.Daemon, error)
|
||||
Delete(name string) error
|
||||
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// daemons implements DaemonsNamespacer interface
|
||||
type daemons struct {
|
||||
r *Client
|
||||
r *ExperimentalClient
|
||||
ns string
|
||||
}
|
||||
|
||||
func newDaemons(c *Client, namespace string) *daemons {
|
||||
func newDaemons(c *ExperimentalClient, namespace string) *daemons {
|
||||
return &daemons{c, namespace}
|
||||
}
|
||||
|
||||
// Ensure statically that daemons implements DaemonInterface.
|
||||
var _ DaemonInterface = &daemons{}
|
||||
|
||||
func (c *daemons) List(selector labels.Selector) (result *api.DaemonList, err error) {
|
||||
result = &api.DaemonList{}
|
||||
func (c *daemons) List(selector labels.Selector) (result *expapi.DaemonList, err error) {
|
||||
result = &expapi.DaemonList{}
|
||||
err = c.r.Get().Namespace(c.ns).Resource("daemons").LabelsSelectorParam(selector).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns information about a particular daemon.
|
||||
func (c *daemons) Get(name string) (result *api.Daemon, err error) {
|
||||
result = &api.Daemon{}
|
||||
func (c *daemons) Get(name string) (result *expapi.Daemon, err error) {
|
||||
result = &expapi.Daemon{}
|
||||
err = c.r.Get().Namespace(c.ns).Resource("daemons").Name(name).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Create creates a new daemon.
|
||||
func (c *daemons) Create(daemon *api.Daemon) (result *api.Daemon, err error) {
|
||||
result = &api.Daemon{}
|
||||
func (c *daemons) Create(daemon *expapi.Daemon) (result *expapi.Daemon, err error) {
|
||||
result = &expapi.Daemon{}
|
||||
err = c.r.Post().Namespace(c.ns).Resource("daemons").Body(daemon).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update updates an existing daemon.
|
||||
func (c *daemons) Update(daemon *api.Daemon) (result *api.Daemon, err error) {
|
||||
result = &api.Daemon{}
|
||||
func (c *daemons) Update(daemon *expapi.Daemon) (result *expapi.Daemon, err error) {
|
||||
result = &expapi.Daemon{}
|
||||
err = c.r.Put().Namespace(c.ns).Resource("daemons").Name(daemon.Name).Body(daemon).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
"k8s.io/kubernetes/pkg/expapi/testapi"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
@ -36,8 +37,8 @@ func TestListDaemons(t *testing.T) {
|
||||
Path: testapi.ResourcePath(getDCResourceName(), ns, ""),
|
||||
},
|
||||
Response: Response{StatusCode: 200,
|
||||
Body: &api.DaemonList{
|
||||
Items: []api.Daemon{
|
||||
Body: &expapi.DaemonList{
|
||||
Items: []expapi.Daemon{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
@ -46,7 +47,7 @@ func TestListDaemons(t *testing.T) {
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: api.DaemonSpec{
|
||||
Spec: expapi.DaemonSpec{
|
||||
Template: &api.PodTemplateSpec{},
|
||||
},
|
||||
},
|
||||
@ -65,7 +66,7 @@ func TestGetDaemon(t *testing.T) {
|
||||
Request: testRequest{Method: "GET", Path: testapi.ResourcePath(getDCResourceName(), ns, "foo"), Query: buildQueryValues(nil)},
|
||||
Response: Response{
|
||||
StatusCode: 200,
|
||||
Body: &api.Daemon{
|
||||
Body: &expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
@ -73,7 +74,7 @@ func TestGetDaemon(t *testing.T) {
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: api.DaemonSpec{
|
||||
Spec: expapi.DaemonSpec{
|
||||
Template: &api.PodTemplateSpec{},
|
||||
},
|
||||
},
|
||||
@ -96,14 +97,14 @@ func TestGetDaemonWithNoName(t *testing.T) {
|
||||
|
||||
func TestUpdateDaemon(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
requestController := &api.Daemon{
|
||||
requestController := &expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
|
||||
}
|
||||
c := &testClient{
|
||||
Request: testRequest{Method: "PUT", Path: testapi.ResourcePath(getDCResourceName(), ns, "foo"), Query: buildQueryValues(nil)},
|
||||
Response: Response{
|
||||
StatusCode: 200,
|
||||
Body: &api.Daemon{
|
||||
Body: &expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
@ -111,7 +112,7 @@ func TestUpdateDaemon(t *testing.T) {
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: api.DaemonSpec{
|
||||
Spec: expapi.DaemonSpec{
|
||||
Template: &api.PodTemplateSpec{},
|
||||
},
|
||||
},
|
||||
@ -133,14 +134,14 @@ func TestDeleteDaemon(t *testing.T) {
|
||||
|
||||
func TestCreateDaemon(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
requestController := &api.Daemon{
|
||||
requestController := &expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
}
|
||||
c := &testClient{
|
||||
Request: testRequest{Method: "POST", Path: testapi.ResourcePath(getDCResourceName(), ns, ""), Body: requestController, Query: buildQueryValues(nil)},
|
||||
Response: Response{
|
||||
StatusCode: 200,
|
||||
Body: &api.Daemon{
|
||||
Body: &expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
@ -148,7 +149,7 @@ func TestCreateDaemon(t *testing.T) {
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: api.DaemonSpec{
|
||||
Spec: expapi.DaemonSpec{
|
||||
Template: &api.PodTemplateSpec{},
|
||||
},
|
||||
},
|
||||
|
@ -34,6 +34,7 @@ type ExperimentalInterface interface {
|
||||
VersionInterface
|
||||
HorizontalPodAutoscalersNamespacer
|
||||
ScaleNamespacer
|
||||
DaemonsNamespacer
|
||||
}
|
||||
|
||||
// ExperimentalClient is used to interact with experimental Kubernetes features.
|
||||
@ -80,6 +81,10 @@ func (c *ExperimentalClient) Scales(namespace string) ScaleInterface {
|
||||
return newScales(c, namespace)
|
||||
}
|
||||
|
||||
func (c *ExperimentalClient) Daemons(namespace string) DaemonInterface {
|
||||
return newDaemons(c, namespace)
|
||||
}
|
||||
|
||||
// NewExperimental creates a new ExperimentalClient for the given config. This client
|
||||
// provides access to experimental Kubernetes features.
|
||||
// Experimental features are not supported and may be changed or removed in
|
||||
|
@ -45,7 +45,6 @@ func TestResourceQuotaCreate(t *testing.T) {
|
||||
api.ResourcePods: resource.MustParse("10"),
|
||||
api.ResourceServices: resource.MustParse("10"),
|
||||
api.ResourceReplicationControllers: resource.MustParse("10"),
|
||||
api.ResourceDaemon: resource.MustParse("10"),
|
||||
api.ResourceQuotas: resource.MustParse("10"),
|
||||
},
|
||||
},
|
||||
@ -78,7 +77,6 @@ func TestResourceQuotaGet(t *testing.T) {
|
||||
api.ResourcePods: resource.MustParse("10"),
|
||||
api.ResourceServices: resource.MustParse("10"),
|
||||
api.ResourceReplicationControllers: resource.MustParse("10"),
|
||||
api.ResourceDaemon: resource.MustParse("10"),
|
||||
api.ResourceQuotas: resource.MustParse("10"),
|
||||
},
|
||||
},
|
||||
@ -135,7 +133,6 @@ func TestResourceQuotaUpdate(t *testing.T) {
|
||||
api.ResourcePods: resource.MustParse("10"),
|
||||
api.ResourceServices: resource.MustParse("10"),
|
||||
api.ResourceReplicationControllers: resource.MustParse("10"),
|
||||
api.ResourceDaemon: resource.MustParse("10"),
|
||||
api.ResourceQuotas: resource.MustParse("10"),
|
||||
},
|
||||
},
|
||||
@ -163,7 +160,6 @@ func TestResourceQuotaStatusUpdate(t *testing.T) {
|
||||
api.ResourcePods: resource.MustParse("10"),
|
||||
api.ResourceServices: resource.MustParse("10"),
|
||||
api.ResourceReplicationControllers: resource.MustParse("10"),
|
||||
api.ResourceDaemon: resource.MustParse("10"),
|
||||
api.ResourceQuotas: resource.MustParse("10"),
|
||||
},
|
||||
},
|
||||
|
@ -17,8 +17,8 @@ limitations under the License.
|
||||
package testclient
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
kClientLib "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
@ -31,40 +31,43 @@ type FakeDaemons struct {
|
||||
Namespace string
|
||||
}
|
||||
|
||||
const (
|
||||
GetDaemonAction = "get-daemon"
|
||||
UpdateDaemonAction = "update-daemon"
|
||||
WatchDaemonAction = "watch-daemon"
|
||||
DeleteDaemonAction = "delete-daemon"
|
||||
ListDaemonAction = "list-daemons"
|
||||
CreateDaemonAction = "create-daemon"
|
||||
)
|
||||
|
||||
// Ensure statically that FakeDaemons implements DaemonInterface.
|
||||
var _ kClientLib.DaemonInterface = &FakeDaemons{}
|
||||
|
||||
func (c *FakeDaemons) Get(name string) (*api.Daemon, error) {
|
||||
obj, err := c.Fake.Invokes(NewGetAction("daemons", c.Namespace, name), &api.Daemon{})
|
||||
return obj.(*api.Daemon), err
|
||||
func (c *FakeDaemons) Get(name string) (*expapi.Daemon, error) {
|
||||
obj, err := c.Fake.Invokes(NewGetAction("daemons", c.Namespace, name), &expapi.Daemon{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*expapi.Daemon), err
|
||||
}
|
||||
|
||||
func (c *FakeDaemons) List(label labels.Selector) (*api.DaemonList, error) {
|
||||
obj, err := c.Fake.Invokes(NewListAction("daemons", c.Namespace, label, nil), &api.DaemonList{})
|
||||
return obj.(*api.DaemonList), err
|
||||
func (c *FakeDaemons) List(label labels.Selector) (*expapi.DaemonList, error) {
|
||||
obj, err := c.Fake.Invokes(NewListAction("daemons", c.Namespace, label, nil), &expapi.DaemonList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*expapi.DaemonList), err
|
||||
}
|
||||
|
||||
func (c *FakeDaemons) Create(daemon *api.Daemon) (*api.Daemon, error) {
|
||||
obj, err := c.Fake.Invokes(NewCreateAction("daemons", c.Namespace, daemon), &api.Daemon{})
|
||||
return obj.(*api.Daemon), err
|
||||
func (c *FakeDaemons) Create(daemon *expapi.Daemon) (*expapi.Daemon, error) {
|
||||
obj, err := c.Fake.Invokes(NewCreateAction("daemons", c.Namespace, daemon), &expapi.Daemon{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*expapi.Daemon), err
|
||||
}
|
||||
|
||||
func (c *FakeDaemons) Update(daemon *api.Daemon) (*api.Daemon, error) {
|
||||
obj, err := c.Fake.Invokes(NewUpdateAction("daemons", c.Namespace, daemon), &api.Daemon{})
|
||||
return obj.(*api.Daemon), err
|
||||
func (c *FakeDaemons) Update(daemon *expapi.Daemon) (*expapi.Daemon, error) {
|
||||
obj, err := c.Fake.Invokes(NewUpdateAction("daemons", c.Namespace, daemon), &expapi.Daemon{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*expapi.Daemon), err
|
||||
}
|
||||
|
||||
func (c *FakeDaemons) Delete(name string) error {
|
||||
_, err := c.Fake.Invokes(NewDeleteAction("daemons", c.Namespace, name), &api.Daemon{})
|
||||
_, err := c.Fake.Invokes(NewDeleteAction("daemons", c.Namespace, name), &expapi.Daemon{})
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@ func addKnownTypes() {
|
||||
&Scale{},
|
||||
&ThirdPartyResource{},
|
||||
&ThirdPartyResourceList{},
|
||||
&DaemonList{},
|
||||
&Daemon{},
|
||||
)
|
||||
}
|
||||
|
||||
@ -47,3 +49,5 @@ func (*ReplicationControllerDummy) IsAnAPIObject() {}
|
||||
func (*Scale) IsAnAPIObject() {}
|
||||
func (*ThirdPartyResource) IsAnAPIObject() {}
|
||||
func (*ThirdPartyResourceList) IsAnAPIObject() {}
|
||||
func (*Daemon) IsAnAPIObject() {}
|
||||
func (*DaemonList) IsAnAPIObject() {}
|
||||
|
@ -271,3 +271,51 @@ type DeploymentList struct {
|
||||
|
||||
Items []Deployment `json:"items"`
|
||||
}
|
||||
|
||||
// DaemonSpec is the specification of a daemon.
|
||||
type DaemonSpec struct {
|
||||
// Selector is a label query over pods that are managed by the daemon.
|
||||
Selector map[string]string `json:"selector"`
|
||||
|
||||
// Template is the object that describes the pod that will be created.
|
||||
// The Daemon will create exactly one copy of this pod on every node
|
||||
// that matches the template's node selector (or on every node if no node
|
||||
// selector is specified).
|
||||
Template *api.PodTemplateSpec `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
// DaemonStatus represents the current status of a daemon.
|
||||
type DaemonStatus struct {
|
||||
// CurrentNumberScheduled is the number of nodes that are running exactly 1 copy of the
|
||||
// daemon and are supposed to run the daemon.
|
||||
CurrentNumberScheduled int `json:"currentNumberScheduled"`
|
||||
|
||||
// NumberMisscheduled is the number of nodes that are running the daemon, but are
|
||||
// not supposed to run the daemon.
|
||||
NumberMisscheduled int `json:"numberMisscheduled"`
|
||||
|
||||
// DesiredNumberScheduled is the total number of nodes that should be running the daemon
|
||||
// (including nodes correctly running the daemon).
|
||||
DesiredNumberScheduled int `json:"desiredNumberScheduled"`
|
||||
}
|
||||
|
||||
// Daemon represents the configuration of a daemon.
|
||||
type Daemon struct {
|
||||
api.TypeMeta `json:",inline"`
|
||||
api.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the desired behavior of this daemon.
|
||||
Spec DaemonSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status is the current status of this daemon. This data may be
|
||||
// out of date by some window of time.
|
||||
Status DaemonStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// DaemonList is a collection of daemon.
|
||||
type DaemonList struct {
|
||||
api.TypeMeta `json:",inline"`
|
||||
api.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []Daemon `json:"items"`
|
||||
}
|
||||
|
@ -24,5 +24,21 @@ func addDefaultingFuncs() {
|
||||
if len(obj.APIGroup) == 0 {
|
||||
obj.APIGroup = "experimental"
|
||||
}
|
||||
})
|
||||
},
|
||||
func(obj *Daemon) {
|
||||
var labels map[string]string
|
||||
if obj.Spec.Template != nil {
|
||||
labels = obj.Spec.Template.Labels
|
||||
}
|
||||
// TODO: support templates defined elsewhere when we support them in the API
|
||||
if labels != nil {
|
||||
if len(obj.Spec.Selector) == 0 {
|
||||
obj.Spec.Selector = labels
|
||||
}
|
||||
if len(obj.Labels) == 0 {
|
||||
obj.Labels = labels
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
104
pkg/expapi/v1/defaults_test.go
Normal file
104
pkg/expapi/v1/defaults_test.go
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 v1
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestSetDefaultDaemon(t *testing.T) {
|
||||
tests := []struct {
|
||||
dc *Daemon
|
||||
expectLabelsChange bool
|
||||
}{
|
||||
{
|
||||
dc: &Daemon{
|
||||
Spec: DaemonSpec{
|
||||
Template: &v1.PodTemplateSpec{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectLabelsChange: true,
|
||||
},
|
||||
{
|
||||
dc: &Daemon{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"bar": "foo",
|
||||
},
|
||||
},
|
||||
Spec: DaemonSpec{
|
||||
Template: &v1.PodTemplateSpec{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectLabelsChange: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
dc := test.dc
|
||||
obj2 := roundTrip(t, runtime.Object(dc))
|
||||
dc2, ok := obj2.(*Daemon)
|
||||
if !ok {
|
||||
t.Errorf("unexpected object: %v", dc2)
|
||||
t.FailNow()
|
||||
}
|
||||
if test.expectLabelsChange != reflect.DeepEqual(dc2.Labels, dc2.Spec.Template.Labels) {
|
||||
if test.expectLabelsChange {
|
||||
t.Errorf("expected: %v, got: %v", dc2.Spec.Template.Labels, dc2.Labels)
|
||||
} else {
|
||||
t.Errorf("unexpected equality: %v", dc.Labels)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
|
||||
data, err := v1.Codec.Encode(obj)
|
||||
if err != nil {
|
||||
t.Errorf("%v\n %#v", err, obj)
|
||||
return nil
|
||||
}
|
||||
obj2, err := api.Codec.Decode(data)
|
||||
if err != nil {
|
||||
t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj)
|
||||
return nil
|
||||
}
|
||||
obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object)
|
||||
err = api.Scheme.Convert(obj2, obj3)
|
||||
if err != nil {
|
||||
t.Errorf("%v\nSource: %#v", err, obj2)
|
||||
return nil
|
||||
}
|
||||
return obj3
|
||||
}
|
@ -40,6 +40,8 @@ func addKnownTypes() {
|
||||
&Scale{},
|
||||
&ThirdPartyResource{},
|
||||
&ThirdPartyResourceList{},
|
||||
&DaemonList{},
|
||||
&Daemon{},
|
||||
)
|
||||
}
|
||||
|
||||
@ -51,3 +53,5 @@ func (*ReplicationControllerDummy) IsAnAPIObject() {}
|
||||
func (*Scale) IsAnAPIObject() {}
|
||||
func (*ThirdPartyResource) IsAnAPIObject() {}
|
||||
func (*ThirdPartyResourceList) IsAnAPIObject() {}
|
||||
func (*Daemon) IsAnAPIObject() {}
|
||||
func (*DaemonList) IsAnAPIObject() {}
|
||||
|
@ -257,3 +257,64 @@ type DeploymentList struct {
|
||||
|
||||
Items []Deployment `json:"items" description:"list of deployments"`
|
||||
}
|
||||
|
||||
// DaemonSpec is the specification of a daemon.
|
||||
type DaemonSpec struct {
|
||||
// Selector is a label query over pods that are managed by the daemon.
|
||||
// Must match in order to be controlled.
|
||||
// If empty, defaulted to labels on Pod template.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors
|
||||
Selector map[string]string `json:"selector,omitempty"`
|
||||
|
||||
// Template is the object that describes the pod that will be created.
|
||||
// The Daemon will create exactly one copy of this pod on every node
|
||||
// that matches the template's node selector (or on every node if no node
|
||||
// selector is specified).
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template
|
||||
Template *v1.PodTemplateSpec `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
// DaemonStatus represents the current status of a daemon.
|
||||
type DaemonStatus struct {
|
||||
// CurrentNumberScheduled is the number of nodes that are running exactly 1 copy of the
|
||||
// daemon and are supposed to run the daemon.
|
||||
CurrentNumberScheduled int `json:"currentNumberScheduled"`
|
||||
|
||||
// NumberMisscheduled is the number of nodes that are running the daemon, but are
|
||||
// not supposed to run the daemon.
|
||||
NumberMisscheduled int `json:"numberMisscheduled"`
|
||||
|
||||
// DesiredNumberScheduled is the total number of nodes that should be running the daemon
|
||||
// (including nodes correctly running the daemon).
|
||||
DesiredNumberScheduled int `json:"desiredNumberScheduled"`
|
||||
}
|
||||
|
||||
// Daemon represents the configuration of a daemon.
|
||||
type Daemon struct {
|
||||
v1.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
|
||||
v1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the specification of the desired behavior of this daemon.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
|
||||
Spec DaemonSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status is the current status of this daemon. This data may be
|
||||
// out of date by some window of time.
|
||||
// Populated by the system.
|
||||
// Read-only.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
|
||||
Status DaemonStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// DaemonList is a collection of daemon.
|
||||
type DaemonList struct {
|
||||
v1.TypeMeta `json:",inline"`
|
||||
// Standard list metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
|
||||
v1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Items is a list of daemons.
|
||||
Items []Daemon `json:"items"`
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
apivalidation "k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
errs "k8s.io/kubernetes/pkg/util/fielderrors"
|
||||
)
|
||||
@ -89,3 +90,69 @@ func ValidateThirdPartyResource(obj *expapi.ThirdPartyResource) errs.ValidationE
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemon tests if required fields in the daemon are set.
|
||||
func ValidateDaemon(controller *expapi.Daemon) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&controller.ObjectMeta, true, apivalidation.ValidateReplicationControllerName).Prefix("metadata")...)
|
||||
allErrs = append(allErrs, ValidateDaemonSpec(&controller.Spec).Prefix("spec")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemonUpdate tests if required fields in the daemon are set.
|
||||
func ValidateDaemonUpdate(oldController, controller *expapi.Daemon) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta).Prefix("metadata")...)
|
||||
allErrs = append(allErrs, ValidateDaemonSpec(&controller.Spec).Prefix("spec")...)
|
||||
allErrs = append(allErrs, ValidateDaemonTemplateUpdate(oldController.Spec.Template, controller.Spec.Template).Prefix("spec.template")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemonTemplateUpdate tests that certain fields in the daemon's pod template are not updated.
|
||||
func ValidateDaemonTemplateUpdate(oldPodTemplate, podTemplate *api.PodTemplateSpec) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
podSpec := podTemplate.Spec
|
||||
// podTemplate.Spec is not a pointer, so we can modify NodeSelector and NodeName directly.
|
||||
podSpec.NodeSelector = oldPodTemplate.Spec.NodeSelector
|
||||
podSpec.NodeName = oldPodTemplate.Spec.NodeName
|
||||
// In particular, we do not allow updates to container images at this point.
|
||||
if !api.Semantic.DeepEqual(oldPodTemplate.Spec, podSpec) {
|
||||
// TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("spec", "content of spec is not printed out, please refer to the \"details\"", "may not update fields other than spec.nodeSelector"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemonSpec tests if required fields in the daemon spec are set.
|
||||
func ValidateDaemonSpec(spec *expapi.DaemonSpec) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
selector := labels.Set(spec.Selector).AsSelector()
|
||||
if selector.Empty() {
|
||||
allErrs = append(allErrs, errs.NewFieldRequired("selector"))
|
||||
}
|
||||
|
||||
if spec.Template == nil {
|
||||
allErrs = append(allErrs, errs.NewFieldRequired("template"))
|
||||
} else {
|
||||
labels := labels.Set(spec.Template.Labels)
|
||||
if !selector.Matches(labels) {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("template.metadata.labels", spec.Template.Labels, "selector does not match template"))
|
||||
}
|
||||
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(spec.Template).Prefix("template")...)
|
||||
// Daemons typically run on more than one node, so mark Read-Write persistent disks as invalid.
|
||||
allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(spec.Template.Spec.Volumes).Prefix("template.spec.volumes")...)
|
||||
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
|
||||
if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways {
|
||||
allErrs = append(allErrs, errs.NewFieldValueNotSupported("template.spec.restartPolicy", spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateDaemonName can be used to check whether the given daemon name is valid.
|
||||
// Prefix indicates this name will be used as part of generation, in which case
|
||||
// trailing dashes are allowed.
|
||||
func ValidateDaemonName(name string, prefix bool) (bool, string) {
|
||||
return apivalidation.NameIsDNSSubdomain(name, prefix)
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
errors "k8s.io/kubernetes/pkg/util/fielderrors"
|
||||
)
|
||||
|
||||
func TestValidateHorizontalPodAutoscaler(t *testing.T) {
|
||||
@ -127,3 +128,415 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDaemonUpdate(t *testing.T) {
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validSelector2 := map[string]string{"c": "d"}
|
||||
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||
|
||||
validPodSpecAbc := api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
}
|
||||
validPodSpecDef := api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "def", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
}
|
||||
validPodSpecNodeSelector := api.PodSpec{
|
||||
NodeSelector: validSelector,
|
||||
NodeName: "xyz",
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
}
|
||||
validPodSpecVolume := api.PodSpec{
|
||||
Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
}
|
||||
|
||||
validPodTemplateAbc := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: validPodSpecAbc,
|
||||
},
|
||||
}
|
||||
validPodTemplateNodeSelector := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: validPodSpecNodeSelector,
|
||||
},
|
||||
}
|
||||
validPodTemplateAbc2 := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector2,
|
||||
},
|
||||
Spec: validPodSpecAbc,
|
||||
},
|
||||
}
|
||||
validPodTemplateDef := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector2,
|
||||
},
|
||||
Spec: validPodSpecDef,
|
||||
},
|
||||
}
|
||||
invalidPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: invalidSelector,
|
||||
},
|
||||
},
|
||||
}
|
||||
readWriteVolumePodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: validPodSpecVolume,
|
||||
},
|
||||
}
|
||||
|
||||
type dcUpdateTest struct {
|
||||
old expapi.Daemon
|
||||
update expapi.Daemon
|
||||
}
|
||||
successCases := []dcUpdateTest{
|
||||
{
|
||||
old: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
old: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector2,
|
||||
Template: &validPodTemplateAbc2.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
old: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateNodeSelector.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
successCase.old.ObjectMeta.ResourceVersion = "1"
|
||||
successCase.update.ObjectMeta.ResourceVersion = "1"
|
||||
if errs := ValidateDaemonUpdate(&successCase.old, &successCase.update); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
errorCases := map[string]dcUpdateTest{
|
||||
"change daemon name": {
|
||||
old: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid selector": {
|
||||
old: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: invalidSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid pod": {
|
||||
old: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &invalidPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"change container image": {
|
||||
old: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateDef.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"read-write volume": {
|
||||
old: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplateAbc.Template,
|
||||
},
|
||||
},
|
||||
update: expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &readWriteVolumePodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for testName, errorCase := range errorCases {
|
||||
if errs := ValidateDaemonUpdate(&errorCase.old, &errorCase.update); len(errs) == 0 {
|
||||
t.Errorf("expected failure: %s", testName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDaemon(t *testing.T) {
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||
invalidPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: invalidSelector,
|
||||
},
|
||||
},
|
||||
}
|
||||
successCases := []expapi.Daemon{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
if errs := ValidateDaemon(&successCase); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
errorCases := map[string]expapi.Daemon{
|
||||
"zero-length ID": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"missing-namespace": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc-123"},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"empty selector": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"selector_doesnt_match": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid manifest": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
},
|
||||
},
|
||||
"invalid_label": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Labels: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid_label 2": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Labels: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Template: &invalidPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid_annotation": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Annotations: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid restart policy 1": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid restart policy 2": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: validSelector,
|
||||
Template: &api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyNever,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
errs := ValidateDaemon(&v)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected failure for %s", k)
|
||||
}
|
||||
for i := range errs {
|
||||
field := errs[i].(*errors.ValidationError).Field
|
||||
if !strings.HasPrefix(field, "spec.template.") &&
|
||||
field != "metadata.name" &&
|
||||
field != "metadata.namespace" &&
|
||||
field != "spec.selector" &&
|
||||
field != "spec.template" &&
|
||||
field != "GCEPersistentDisk.ReadOnly" &&
|
||||
field != "spec.template.labels" &&
|
||||
field != "metadata.annotations" &&
|
||||
field != "metadata.labels" {
|
||||
t.Errorf("%s: missing prefix for: %v", k, errs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package etcd
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/daemon"
|
||||
@ -39,10 +40,10 @@ var daemonPrefix = "/daemons"
|
||||
// NewREST returns a RESTStorage object that will work against daemons.
|
||||
func NewREST(s storage.Interface) *REST {
|
||||
store := &etcdgeneric.Etcd{
|
||||
NewFunc: func() runtime.Object { return &api.Daemon{} },
|
||||
NewFunc: func() runtime.Object { return &expapi.Daemon{} },
|
||||
|
||||
// NewListFunc returns an object capable of storing results of an etcd list.
|
||||
NewListFunc: func() runtime.Object { return &api.DaemonList{} },
|
||||
NewListFunc: func() runtime.Object { return &expapi.DaemonList{} },
|
||||
// Produces a path that etcd understands, to the root of the resource
|
||||
// by combining the namespace in the context with the given prefix
|
||||
KeyRootFunc: func(ctx api.Context) string {
|
||||
@ -55,7 +56,7 @@ func NewREST(s storage.Interface) *REST {
|
||||
},
|
||||
// Retrieve the name field of a daemon
|
||||
ObjectNameFunc: func(obj runtime.Object) (string, error) {
|
||||
return obj.(*api.Daemon).Name, nil
|
||||
return obj.(*expapi.Daemon).Name, nil
|
||||
},
|
||||
// Used to match objects based on labels/fields for list and watch
|
||||
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/latest"
|
||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd"
|
||||
@ -46,13 +47,13 @@ func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||
}
|
||||
|
||||
// createController is a helper function that returns a controller with the updated resource version.
|
||||
func createController(storage *REST, dc api.Daemon, t *testing.T) (api.Daemon, error) {
|
||||
func createController(storage *REST, dc expapi.Daemon, t *testing.T) (expapi.Daemon, error) {
|
||||
ctx := api.WithNamespace(api.NewContext(), dc.Namespace)
|
||||
obj, err := storage.Create(ctx, &dc)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create controller, %v", err)
|
||||
}
|
||||
newDc := obj.(*api.Daemon)
|
||||
newDc := obj.(*expapi.Daemon)
|
||||
return *newDc, nil
|
||||
}
|
||||
|
||||
@ -75,12 +76,12 @@ var validPodTemplate = api.PodTemplate{
|
||||
},
|
||||
}
|
||||
|
||||
var validControllerSpec = api.DaemonSpec{
|
||||
var validControllerSpec = expapi.DaemonSpec{
|
||||
Selector: validPodTemplate.Template.Labels,
|
||||
Template: &validPodTemplate.Template,
|
||||
}
|
||||
|
||||
var validController = api.Daemon{
|
||||
var validController = expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||
Spec: validControllerSpec,
|
||||
}
|
||||
@ -90,8 +91,8 @@ func TestCreate(t *testing.T) {
|
||||
test := resttest.New(t, storage, fakeClient.SetError)
|
||||
test.TestCreate(
|
||||
// valid
|
||||
&api.Daemon{
|
||||
Spec: api.DaemonSpec{
|
||||
&expapi.Daemon{
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: map[string]string{"a": "b"},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
@ -103,8 +104,8 @@ func TestCreate(t *testing.T) {
|
||||
return registrytest.GetObject(fakeClient, storage.KeyFunc, storage.NewFunc, ctx, obj)
|
||||
},
|
||||
// invalid
|
||||
&api.Daemon{
|
||||
Spec: api.DaemonSpec{
|
||||
&expapi.Daemon{
|
||||
Spec: expapi.DaemonSpec{
|
||||
Selector: map[string]string{},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
@ -133,7 +134,7 @@ func TestEtcdGetController(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
controller, ok := ctrl.(*api.Daemon)
|
||||
controller, ok := ctrl.(*expapi.Daemon)
|
||||
if !ok {
|
||||
t.Errorf("Expected a controller, got %#v", ctrl)
|
||||
}
|
||||
@ -151,20 +152,20 @@ func TestEtcdControllerValidatesUpdate(t *testing.T) {
|
||||
t.Errorf("Failed to create controller, cannot proceed with test.")
|
||||
}
|
||||
|
||||
updaters := []func(dc api.Daemon) (runtime.Object, bool, error){
|
||||
func(dc api.Daemon) (runtime.Object, bool, error) {
|
||||
updaters := []func(dc expapi.Daemon) (runtime.Object, bool, error){
|
||||
func(dc expapi.Daemon) (runtime.Object, bool, error) {
|
||||
dc.UID = "newUID"
|
||||
return storage.Update(ctx, &dc)
|
||||
},
|
||||
func(dc api.Daemon) (runtime.Object, bool, error) {
|
||||
func(dc expapi.Daemon) (runtime.Object, bool, error) {
|
||||
dc.Name = ""
|
||||
return storage.Update(ctx, &dc)
|
||||
},
|
||||
func(dc api.Daemon) (runtime.Object, bool, error) {
|
||||
func(dc expapi.Daemon) (runtime.Object, bool, error) {
|
||||
dc.Spec.Template.Spec.RestartPolicy = api.RestartPolicyOnFailure
|
||||
return storage.Update(ctx, &dc)
|
||||
},
|
||||
func(dc api.Daemon) (runtime.Object, bool, error) {
|
||||
func(dc expapi.Daemon) (runtime.Object, bool, error) {
|
||||
dc.Spec.Selector = map[string]string{}
|
||||
return storage.Update(ctx, &dc)
|
||||
},
|
||||
@ -224,7 +225,7 @@ func TestEtcdGetControllerDifferentNamespace(t *testing.T) {
|
||||
fakeClient.Set(key2, runtime.EncodeOrDie(latest.Codec, &otherNsController), 0)
|
||||
|
||||
obj, err := storage.Get(ctx1, validController.Name)
|
||||
ctrl1, _ := obj.(*api.Daemon)
|
||||
ctrl1, _ := obj.(*expapi.Daemon)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
@ -236,7 +237,7 @@ func TestEtcdGetControllerDifferentNamespace(t *testing.T) {
|
||||
}
|
||||
|
||||
obj, err = storage.Get(ctx2, validController.Name)
|
||||
ctrl2, _ := obj.(*api.Daemon)
|
||||
ctrl2, _ := obj.(*expapi.Daemon)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
@ -320,7 +321,7 @@ func TestEtcdListControllers(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
controllers, _ := objList.(*api.DaemonList)
|
||||
controllers, _ := objList.(*expapi.DaemonList)
|
||||
if len(controllers.Items) != 2 || controllers.Items[0].Name != validController.Name || controllers.Items[1].Name != controller.Name {
|
||||
t.Errorf("Unexpected controller list: %#v", controllers)
|
||||
}
|
||||
@ -340,7 +341,7 @@ func TestEtcdListControllersNotFound(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
controllers, _ := objList.(*api.DaemonList)
|
||||
controllers, _ := objList.(*expapi.DaemonList)
|
||||
if len(controllers.Items) != 0 {
|
||||
t.Errorf("Unexpected controller list: %#v", controllers)
|
||||
}
|
||||
@ -376,7 +377,7 @@ func TestEtcdListControllersLabelsMatch(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
controllers, _ := objList.(*api.DaemonList)
|
||||
controllers, _ := objList.(*expapi.DaemonList)
|
||||
if len(controllers.Items) != 1 || controllers.Items[0].Name != controller.Name ||
|
||||
!testLabels.Matches(labels.Set(controllers.Items[0].Labels)) {
|
||||
t.Errorf("Unexpected controller list: %#v for query with labels %#v",
|
||||
@ -430,7 +431,7 @@ func TestEtcdWatchControllersMatch(t *testing.T) {
|
||||
// The watcher above is waiting for these Labels, on receiving them it should
|
||||
// apply the ControllerStatus decorator, which lists pods, causing a query against
|
||||
// the /registry/pods endpoint of the etcd client.
|
||||
controller := &api.Daemon{
|
||||
controller := &expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: validController.Spec.Selector,
|
||||
@ -476,13 +477,13 @@ func TestEtcdWatchControllersFields(t *testing.T) {
|
||||
etcdstorage.EtcdCAS,
|
||||
etcdstorage.EtcdDelete}
|
||||
|
||||
controller := &api.Daemon{
|
||||
controller := &expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: validController.Spec.Selector,
|
||||
Namespace: "default",
|
||||
},
|
||||
Status: api.DaemonStatus{
|
||||
Status: expapi.DaemonStatus{
|
||||
CurrentNumberScheduled: 2,
|
||||
NumberMisscheduled: 1,
|
||||
DesiredNumberScheduled: 4,
|
||||
@ -549,7 +550,7 @@ func TestEtcdWatchControllersNotMatch(t *testing.T) {
|
||||
}
|
||||
fakeClient.WaitForWatchCompletion()
|
||||
|
||||
controller := &api.Daemon{
|
||||
controller := &expapi.Daemon{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "bar",
|
||||
Labels: map[string]string{
|
||||
|
@ -21,7 +21,8 @@ import (
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/expapi"
|
||||
"k8s.io/kubernetes/pkg/expapi/validation"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
@ -45,16 +46,16 @@ func (daemonStrategy) NamespaceScoped() bool {
|
||||
|
||||
// PrepareForCreate clears the status of a daemon before creation.
|
||||
func (daemonStrategy) PrepareForCreate(obj runtime.Object) {
|
||||
daemon := obj.(*api.Daemon)
|
||||
daemon.Status = api.DaemonStatus{}
|
||||
daemon := obj.(*expapi.Daemon)
|
||||
daemon.Status = expapi.DaemonStatus{}
|
||||
|
||||
daemon.Generation = 1
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (daemonStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
newDaemon := obj.(*api.Daemon)
|
||||
oldDaemon := old.(*api.Daemon)
|
||||
newDaemon := obj.(*expapi.Daemon)
|
||||
oldDaemon := old.(*expapi.Daemon)
|
||||
|
||||
// Any changes to the spec increment the generation number, any changes to the
|
||||
// status should reflect the generation number of the corresponding object. We push
|
||||
@ -74,7 +75,7 @@ func (daemonStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
|
||||
// Validate validates a new daemon.
|
||||
func (daemonStrategy) Validate(ctx api.Context, obj runtime.Object) fielderrors.ValidationErrorList {
|
||||
daemon := obj.(*api.Daemon)
|
||||
daemon := obj.(*expapi.Daemon)
|
||||
return validation.ValidateDaemon(daemon)
|
||||
}
|
||||
|
||||
@ -86,8 +87,8 @@ func (daemonStrategy) AllowCreateOnUpdate() bool {
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (daemonStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||
validationErrorList := validation.ValidateDaemon(obj.(*api.Daemon))
|
||||
updateErrorList := validation.ValidateDaemonUpdate(old.(*api.Daemon), obj.(*api.Daemon))
|
||||
validationErrorList := validation.ValidateDaemon(obj.(*expapi.Daemon))
|
||||
updateErrorList := validation.ValidateDaemonUpdate(old.(*expapi.Daemon), obj.(*expapi.Daemon))
|
||||
return append(validationErrorList, updateErrorList...)
|
||||
}
|
||||
|
||||
@ -97,7 +98,7 @@ func (daemonStrategy) AllowUnconditionalUpdate() bool {
|
||||
}
|
||||
|
||||
// DaemonToSelectableFields returns a field set that represents the object.
|
||||
func DaemonToSelectableFields(daemon *api.Daemon) fields.Set {
|
||||
func DaemonToSelectableFields(daemon *expapi.Daemon) fields.Set {
|
||||
return fields.Set{
|
||||
"metadata.name": daemon.Name,
|
||||
}
|
||||
@ -111,7 +112,7 @@ func MatchDaemon(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
Label: label,
|
||||
Field: field,
|
||||
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||
daemon, ok := obj.(*api.Daemon)
|
||||
daemon, ok := obj.(*expapi.Daemon)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("given object is not a daemon.")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user