diff --git a/cmd/kubelet/kubelet.go b/cmd/kubelet/kubelet.go index b3e7a3069ae..d8a919be680 100644 --- a/cmd/kubelet/kubelet.go +++ b/cmd/kubelet/kubelet.go @@ -163,9 +163,11 @@ func main() { float32(*registryPullQPS), *registryBurst) - health.AddHealthChecker("exec", health.NewExecHealthChecker(k)) - health.AddHealthChecker("http", health.NewHTTPHealthChecker(&http.Client{})) - health.AddHealthChecker("tcp", &health.TCPHealthChecker{}) + // TODO: These should probably become more plugin-ish: register a factory func + // in each checker's init(), iterate those here. + health.AddHealthChecker(health.NewExecHealthChecker(k)) + health.AddHealthChecker(health.NewHTTPHealthChecker(&http.Client{})) + health.AddHealthChecker(&health.TCPHealthChecker{}) // start the kubelet go util.Forever(func() { k.Run(cfg.Updates()) }, 0) diff --git a/pkg/api/errors/errors_test.go b/pkg/api/errors/errors_test.go index 49edec7880d..01a0a39aaae 100644 --- a/pkg/api/errors/errors_test.go +++ b/pkg/api/errors/errors_test.go @@ -29,26 +29,26 @@ import ( func TestErrorNew(t *testing.T) { err := NewAlreadyExists("test", "1") if !IsAlreadyExists(err) { - t.Errorf("expected to be already_exists") + t.Errorf("expected to be %s", api.StatusReasonAlreadyExists) } if IsConflict(err) { - t.Errorf("expected to not be confict") + t.Errorf("expected to not be %s", api.StatusReasonConflict) } if IsNotFound(err) { t.Errorf(fmt.Sprintf("expected to not be %s", api.StatusReasonNotFound)) } if IsInvalid(err) { - t.Errorf("expected to not be invalid") + t.Errorf("expected to not be %s", api.StatusReasonInvalid) } if !IsConflict(NewConflict("test", "2", errors.New("message"))) { t.Errorf("expected to be conflict") } if !IsNotFound(NewNotFound("test", "3")) { - t.Errorf("expected to be not found") + t.Errorf("expected to be %s", api.StatusReasonNotFound) } if !IsInvalid(NewInvalid("test", "2", nil)) { - t.Errorf("expected to be invalid") + t.Errorf("expected to be %s", api.StatusReasonInvalid) } } @@ -122,7 +122,7 @@ func TestNewInvalid(t *testing.T) { t.Errorf("%d: unexpected status: %#v", i, status) } if !reflect.DeepEqual(expected, status.Details) { - t.Errorf("%d: expected %#v, got %#v", expected, status.Details) + t.Errorf("%d: expected %#v, got %#v", i, expected, status.Details) } } } diff --git a/pkg/api/errors/validation.go b/pkg/api/errors/validation.go index 6626f0c76bb..bd9ac72d53c 100644 --- a/pkg/api/errors/validation.go +++ b/pkg/api/errors/validation.go @@ -28,22 +28,23 @@ import ( // CauseType in api/types.go. type ValidationErrorType string +// TODO: These values are duplicated in api/types.go, but there's a circular dep. Fix it. const ( // ValidationErrorTypeNotFound is used to report failure to find a requested value // (e.g. looking up an ID). - ValidationErrorTypeNotFound ValidationErrorType = "fieldValueNotFound" + ValidationErrorTypeNotFound ValidationErrorType = "FieldValueNotFound" // ValidationErrorTypeRequired is used to report required values that are not // provided (e.g. empty strings, null values, or empty arrays). - ValidationErrorTypeRequired ValidationErrorType = "fieldValueRequired" + ValidationErrorTypeRequired ValidationErrorType = "FieldValueRequired" // ValidationErrorTypeDuplicate is used to report collisions of values that must be // unique (e.g. unique IDs). - ValidationErrorTypeDuplicate ValidationErrorType = "fieldValueDuplicate" + ValidationErrorTypeDuplicate ValidationErrorType = "FieldValueDuplicate" // ValidationErrorTypeInvalid is used to report malformed values (e.g. failed regex // match). - ValidationErrorTypeInvalid ValidationErrorType = "fieldValueInvalid" + ValidationErrorTypeInvalid ValidationErrorType = "FieldValueInvalid" // ValidationErrorTypeNotSupported is used to report valid (as per formatting rules) // values that can not be handled (e.g. an enumerated string). - ValidationErrorTypeNotSupported ValidationErrorType = "fieldValueNotSupported" + ValidationErrorTypeNotSupported ValidationErrorType = "FieldValueNotSupported" ) func ValueOf(t ValidationErrorType) string { diff --git a/pkg/api/types.go b/pkg/api/types.go index 1d9a41e8051..a081f0a7e12 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -100,6 +100,16 @@ type HostDirectory struct { type EmptyDirectory struct{} +// Protocol defines network protocols supported for things like conatiner ports. +type Protocol string + +const ( + // ProtocolTCP is the TCP protocol. + ProtocolTCP Protocol = "TCP" + // ProtocolUDP is the UDP protocol. + ProtocolUDP Protocol = "UDP" +) + // Port represents a network port in a single container. type Port struct { // Optional: If specified, this must be a DNS_LABEL. Each named port @@ -109,8 +119,8 @@ type Port struct { HostPort int `yaml:"hostPort,omitempty" json:"hostPort,omitempty"` // Required: This must be a valid port number, 0 < x < 65536. ContainerPort int `yaml:"containerPort" json:"containerPort"` - // Optional: Supports "TCP" and "UDP". Defaults to "TCP". - Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"` + // Optional: Defaults to "TCP". + Protocol Protocol `yaml:"protocol,omitempty" json:"protocol,omitempty"` // Optional: What host IP to bind the external port to. HostIP string `yaml:"hostIP,omitempty" json:"hostIP,omitempty"` } @@ -161,8 +171,6 @@ type ExecAction struct { // LivenessProbe describes a liveness probe to be examined to the container. // TODO: pass structured data to the actions, and document that data here. type LivenessProbe struct { - // Type of liveness probe. Current legal values "http", "tcp" - Type string `yaml:"type,omitempty" json:"type,omitempty"` // HTTPGetProbe parameters, required if Type == 'http' HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"` // TCPSocketProbe parameter, required if Type == 'tcp' @@ -381,8 +389,8 @@ type Service struct { // Required. Port int `json:"port" yaml:"port"` - // Optional: Supports "TCP" and "UDP". Defaults to "TCP". - Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"` + // Optional: Defaults to "TCP". + Protocol Protocol `yaml:"protocol,omitempty" json:"protocol,omitempty"` // This service's labels. Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` @@ -447,12 +455,12 @@ func (*Binding) IsAnAPIObject() {} // import both. type Status struct { JSONBase `json:",inline" yaml:",inline"` - // One of: "success", "failure", "working" (for operations not yet completed) + // One of: "Success", "Failure", "Working" (for operations not yet completed) Status string `json:"status,omitempty" yaml:"status,omitempty"` // A human-readable description of the status of this operation. Message string `json:"message,omitempty" yaml:"message,omitempty"` // A machine-readable description of why this operation is in the - // "failure" or "working" status. If this value is empty there + // "Failure" or "Working" status. If this value is empty there // is no information available. A Reason clarifies an HTTP status // code but does not override it. Reason StatusReason `json:"reason,omitempty" yaml:"reason,omitempty"` @@ -487,9 +495,9 @@ type StatusDetails struct { // Values of Status.Status const ( - StatusSuccess = "success" - StatusFailure = "failure" - StatusWorking = "working" + StatusSuccess = "Success" + StatusFailure = "Failure" + StatusWorking = "Working" ) // StatusReason is an enumeration of possible failure causes. Each StatusReason @@ -514,7 +522,7 @@ const ( // "Location" - HTTP header populated with a URL that can retrieved the final // status of this operation. // Status code 202 - StatusReasonWorking StatusReason = "working" + StatusReasonWorking StatusReason = "Working" // StatusReasonNotFound means one or more resources required for this operation // could not be found. @@ -524,21 +532,21 @@ const ( // resource. // "id" string - the identifier of the missing resource // Status code 404 - StatusReasonNotFound StatusReason = "not_found" + StatusReasonNotFound StatusReason = "NotFound" // StatusReasonAlreadyExists means the resource you are creating already exists. // Details (optional): // "kind" string - the kind attribute of the conflicting resource // "id" string - the identifier of the conflicting resource // Status code 409 - StatusReasonAlreadyExists StatusReason = "already_exists" + StatusReasonAlreadyExists StatusReason = "AlreadyExists" // StatusReasonConflict means the requested update operation cannot be completed // due to a conflict in the operation. The client may need to alter the request. // Each resource may define custom details that indicate the nature of the // conflict. // Status code 409 - StatusReasonConflict StatusReason = "conflict" + StatusReasonConflict StatusReason = "Conflict" // StatusReasonInvalid means the requested create or update operation cannot be // completed due to invalid data provided as part of the request. The client may @@ -551,7 +559,7 @@ const ( // provided resource that was invalid. The code, message, and // field attributes will be set. // Status code 422 - StatusReasonInvalid StatusReason = "invalid" + StatusReasonInvalid StatusReason = "Invalid" ) // StatusCause provides more information about an api.Status failure, including @@ -577,25 +585,25 @@ type StatusCause struct { // CauseType is a machine readable value providing more detail about what // occured in a status response. An operation may have multiple causes for a -// status (whether failure, success, or working). +// status (whether Failure, Success, or Working). type CauseType string const ( // CauseTypeFieldValueNotFound is used to report failure to find a requested value // (e.g. looking up an ID). - CauseTypeFieldValueNotFound CauseType = "fieldValueNotFound" + CauseTypeFieldValueNotFound CauseType = "FieldValueNotFound" // CauseTypeFieldValueInvalid is used to report required values that are not // provided (e.g. empty strings, null values, or empty arrays). - CauseTypeFieldValueRequired CauseType = "fieldValueRequired" + CauseTypeFieldValueRequired CauseType = "FieldValueRequired" // CauseTypeFieldValueDuplicate is used to report collisions of values that must be // unique (e.g. unique IDs). - CauseTypeFieldValueDuplicate CauseType = "fieldValueDuplicate" + CauseTypeFieldValueDuplicate CauseType = "FieldValueDuplicate" // CauseTypeFieldValueInvalid is used to report malformed values (e.g. failed regex // match). - CauseTypeFieldValueInvalid CauseType = "fieldValueInvalid" + CauseTypeFieldValueInvalid CauseType = "FieldValueInvalid" // CauseTypeFieldValueNotSupported is used to report valid (as per formatting rules) // values that can not be handled (e.g. an enumerated string). - CauseTypeFieldValueNotSupported CauseType = "fieldValueNotSupported" + CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported" ) // ServerOp is an operation delivered to API clients. diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 61277c3e801..12131d35ccf 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -100,6 +100,16 @@ type HostDirectory struct { type EmptyDirectory struct{} +// Protocol defines network protocols supported for things like conatiner ports. +type Protocol string + +const ( + // ProtocolTCP is the TCP protocol. + ProtocolTCP Protocol = "TCP" + // ProtocolUDP is the UDP protocol. + ProtocolUDP Protocol = "UDP" +) + // Port represents a network port in a single container. type Port struct { // Optional: If specified, this must be a DNS_LABEL. Each named port @@ -109,8 +119,8 @@ type Port struct { HostPort int `yaml:"hostPort,omitempty" json:"hostPort,omitempty"` // Required: This must be a valid port number, 0 < x < 65536. ContainerPort int `yaml:"containerPort" json:"containerPort"` - // Optional: Supports "TCP" and "UDP". Defaults to "TCP". - Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"` + // Optional: Defaults to "TCP". + Protocol Protocol `yaml:"protocol,omitempty" json:"protocol,omitempty"` // Optional: What host IP to bind the external port to. HostIP string `yaml:"hostIP,omitempty" json:"hostIP,omitempty"` } @@ -171,8 +181,6 @@ type ExecAction struct { // LivenessProbe describes a liveness probe to be examined to the container. // TODO: pass structured data to the actions, and document that data here. type LivenessProbe struct { - // Type of liveness probe. Current legal values "http", "tcp" - Type string `yaml:"type,omitempty" json:"type,omitempty"` // HTTPGetProbe parameters, required if Type == 'http' HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"` // TCPSocketProbe parameter, required if Type == 'tcp' @@ -394,8 +402,8 @@ type Service struct { // Required. Port int `json:"port" yaml:"port"` - // Optional: Supports "TCP" and "UDP". Defaults to "TCP". - Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"` + // Optional: Defaults to "TCP". + Protocol Protocol `yaml:"protocol,omitempty" json:"protocol,omitempty"` // This service's labels. Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` @@ -463,12 +471,12 @@ func (*Binding) IsAnAPIObject() {} // import both. type Status struct { JSONBase `json:",inline" yaml:",inline"` - // One of: "success", "failure", "working" (for operations not yet completed) + // One of: "Success", "Failure", "Working" (for operations not yet completed) Status string `json:"status,omitempty" yaml:"status,omitempty"` // A human-readable description of the status of this operation. Message string `json:"message,omitempty" yaml:"message,omitempty"` // A machine-readable description of why this operation is in the - // "failure" or "working" status. If this value is empty there + // "Failure" or "Working" status. If this value is empty there // is no information available. A Reason clarifies an HTTP status // code but does not override it. Reason StatusReason `json:"reason,omitempty" yaml:"reason,omitempty"` @@ -503,9 +511,9 @@ type StatusDetails struct { // Values of Status.Status const ( - StatusSuccess = "success" - StatusFailure = "failure" - StatusWorking = "working" + StatusSuccess = "Success" + StatusFailure = "Failure" + StatusWorking = "Working" ) // StatusReason is an enumeration of possible failure causes. Each StatusReason @@ -530,7 +538,7 @@ const ( // "Location" - HTTP header populated with a URL that can retrieved the final // status of this operation. // Status code 202 - StatusReasonWorking StatusReason = "working" + StatusReasonWorking StatusReason = "Working" // StatusReasonNotFound means one or more resources required for this operation // could not be found. @@ -540,21 +548,21 @@ const ( // resource. // "id" string - the identifier of the missing resource // Status code 404 - StatusReasonNotFound StatusReason = "notFound" + StatusReasonNotFound StatusReason = "NotFound" // StatusReasonAlreadyExists means the resource you are creating already exists. // Details (optional): // "kind" string - the kind attribute of the conflicting resource // "id" string - the identifier of the conflicting resource // Status code 409 - StatusReasonAlreadyExists StatusReason = "alreadyExists" + StatusReasonAlreadyExists StatusReason = "AlreadyExists" // StatusReasonConflict means the requested update operation cannot be completed // due to a conflict in the operation. The client may need to alter the request. // Each resource may define custom details that indicate the nature of the // conflict. // Status code 409 - StatusReasonConflict StatusReason = "conflict" + StatusReasonConflict StatusReason = "Conflict" ) // StatusCause provides more information about an api.Status failure, including @@ -580,25 +588,25 @@ type StatusCause struct { // CauseType is a machine readable value providing more detail about what // occured in a status response. An operation may have multiple causes for a -// status (whether failure, success, or working). +// status (whether Failure, Success, or Working). type CauseType string const ( // CauseTypeFieldValueNotFound is used to report failure to find a requested value // (e.g. looking up an ID). - CauseTypeFieldValueNotFound CauseType = "fieldValueNotFound" + CauseTypeFieldValueNotFound CauseType = "FieldValueNotFound" // CauseTypeFieldValueInvalid is used to report required values that are not // provided (e.g. empty strings, null values, or empty arrays). - CauseTypeFieldValueRequired CauseType = "fieldValueRequired" + CauseTypeFieldValueRequired CauseType = "FieldValueRequired" // CauseTypeFieldValueDuplicate is used to report collisions of values that must be // unique (e.g. unique IDs). - CauseTypeFieldValueDuplicate CauseType = "fieldValueDuplicate" + CauseTypeFieldValueDuplicate CauseType = "FieldValueDuplicate" // CauseTypeFieldValueInvalid is used to report malformed values (e.g. failed regex // match). - CauseTypeFieldValueInvalid CauseType = "fieldValueInvalid" + CauseTypeFieldValueInvalid CauseType = "FieldValueInvalid" // CauseTypeFieldValueNotSupported is used to report valid (as per formatting rules) // values that can not be handled (e.g. an enumerated string). - CauseTypeFieldValueNotSupported CauseType = "fieldValueNotSupported" + CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported" ) // ServerOp is an operation delivered to API clients. diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index 61d066dd4f7..7f320f7c807 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -100,6 +100,16 @@ type HostDirectory struct { type EmptyDirectory struct{} +// Protocol defines network protocols supported for things like conatiner ports. +type Protocol string + +const ( + // ProtocolTCP is the TCP protocol. + ProtocolTCP Protocol = "TCP" + // ProtocolUDP is the UDP protocol. + ProtocolUDP Protocol = "UDP" +) + // Port represents a network port in a single container. type Port struct { // Optional: If specified, this must be a DNS_LABEL. Each named port @@ -109,8 +119,8 @@ type Port struct { HostPort int `yaml:"hostPort,omitempty" json:"hostPort,omitempty"` // Required: This must be a valid port number, 0 < x < 65536. ContainerPort int `yaml:"containerPort" json:"containerPort"` - // Optional: Supports "TCP" and "UDP". Defaults to "TCP". - Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"` + // Optional: Defaults to "TCP". + Protocol Protocol `yaml:"protocol,omitempty" json:"protocol,omitempty"` // Optional: What host IP to bind the external port to. HostIP string `yaml:"hostIP,omitempty" json:"hostIP,omitempty"` } @@ -170,8 +180,6 @@ type ExecAction struct { // LivenessProbe describes a liveness probe to be examined to the container. // TODO: pass structured data to the actions, and document that data here. type LivenessProbe struct { - // Type of liveness probe. Current legal values "http", "tcp" - Type string `yaml:"type,omitempty" json:"type,omitempty"` // HTTPGetProbe parameters, required if Type == 'http' HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"` // TCPSocketProbe parameter, required if Type == 'tcp' @@ -391,8 +399,8 @@ type Service struct { // Required. Port int `json:"port" yaml:"port"` - // Optional: Supports "TCP" and "UDP". Defaults to "TCP". - Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"` + // Optional: Defaults to "TCP". + Protocol Protocol `yaml:"protocol,omitempty" json:"protocol,omitempty"` // This service's labels. Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` @@ -460,12 +468,12 @@ func (*Binding) IsAnAPIObject() {} // import both. type Status struct { JSONBase `json:",inline" yaml:",inline"` - // One of: "success", "failure", "working" (for operations not yet completed) + // One of: "Success", "Failure", "Working" (for operations not yet completed) Status string `json:"status,omitempty" yaml:"status,omitempty"` // A human-readable description of the status of this operation. Message string `json:"message,omitempty" yaml:"message,omitempty"` // A machine-readable description of why this operation is in the - // "failure" or "working" status. If this value is empty there + // "Failure" or "Working" status. If this value is empty there // is no information available. A Reason clarifies an HTTP status // code but does not override it. Reason StatusReason `json:"reason,omitempty" yaml:"reason,omitempty"` @@ -500,9 +508,9 @@ type StatusDetails struct { // Values of Status.Status const ( - StatusSuccess = "success" - StatusFailure = "failure" - StatusWorking = "working" + StatusSuccess = "Success" + StatusFailure = "Failure" + StatusWorking = "Working" ) // StatusReason is an enumeration of possible failure causes. Each StatusReason @@ -527,7 +535,7 @@ const ( // "Location" - HTTP header populated with a URL that can retrieved the final // status of this operation. // Status code 202 - StatusReasonWorking StatusReason = "working" + StatusReasonWorking StatusReason = "Working" // StatusReasonNotFound means one or more resources required for this operation // could not be found. @@ -537,21 +545,21 @@ const ( // resource. // "id" string - the identifier of the missing resource // Status code 404 - StatusReasonNotFound StatusReason = "not_found" + StatusReasonNotFound StatusReason = "NotFound" // StatusReasonAlreadyExists means the resource you are creating already exists. // Details (optional): // "kind" string - the kind attribute of the conflicting resource // "id" string - the identifier of the conflicting resource // Status code 409 - StatusReasonAlreadyExists StatusReason = "already_exists" + StatusReasonAlreadyExists StatusReason = "AlreadyExists" // StatusReasonConflict means the requested update operation cannot be completed // due to a conflict in the operation. The client may need to alter the request. // Each resource may define custom details that indicate the nature of the // conflict. // Status code 409 - StatusReasonConflict StatusReason = "conflict" + StatusReasonConflict StatusReason = "Conflict" // StatusReasonInvalid means the requested create or update operation cannot be // completed due to invalid data provided as part of the request. The client may @@ -564,7 +572,7 @@ const ( // provided resource that was invalid. The code, message, and // field attributes will be set. // Status code 422 - StatusReasonInvalid StatusReason = "invalid" + StatusReasonInvalid StatusReason = "Invalid" ) // StatusCause provides more information about an api.Status failure, including @@ -590,25 +598,25 @@ type StatusCause struct { // CauseType is a machine readable value providing more detail about what // occured in a status response. An operation may have multiple causes for a -// status (whether failure, success, or working). +// status (whether Failure, Success, or Working). type CauseType string const ( // CauseTypeFieldValueNotFound is used to report failure to find a requested value // (e.g. looking up an ID). - CauseTypeFieldValueNotFound CauseType = "fieldValueNotFound" + CauseTypeFieldValueNotFound CauseType = "FieldValueNotFound" // CauseTypeFieldValueInvalid is used to report required values that are not // provided (e.g. empty strings, null values, or empty arrays). - CauseTypeFieldValueRequired CauseType = "fieldValueRequired" + CauseTypeFieldValueRequired CauseType = "FieldValueRequired" // CauseTypeFieldValueDuplicate is used to report collisions of values that must be // unique (e.g. unique IDs). - CauseTypeFieldValueDuplicate CauseType = "fieldValueDuplicate" + CauseTypeFieldValueDuplicate CauseType = "FieldValueDuplicate" // CauseTypeFieldValueInvalid is used to report malformed values (e.g. failed regex // match). - CauseTypeFieldValueInvalid CauseType = "fieldValueInvalid" + CauseTypeFieldValueInvalid CauseType = "FieldValueInvalid" // CauseTypeFieldValueNotSupported is used to report valid (as per formatting rules) // values that can not be handled (e.g. an enumerated string). - CauseTypeFieldValueNotSupported CauseType = "fieldValueNotSupported" + CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported" ) // ServerOp is an operation delivered to API clients. diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 661bc855749..26c75c57b22 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -100,6 +100,16 @@ type HostDirectory struct { type EmptyDirectory struct{} +// Protocol defines network protocols supported for things like conatiner ports. +type Protocol string + +const ( + // ProtocolTCP is the TCP protocol. + ProtocolTCP Protocol = "TCP" + // ProtocolUDP is the UDP protocol. + ProtocolUDP Protocol = "UDP" +) + // Port represents a network port in a single container. type Port struct { // Optional: If specified, this must be a DNS_LABEL. Each named port @@ -109,8 +119,8 @@ type Port struct { HostPort int `yaml:"hostPort,omitempty" json:"hostPort,omitempty"` // Required: This must be a valid port number, 0 < x < 65536. ContainerPort int `yaml:"containerPort" json:"containerPort"` - // Optional: Supports "TCP" and "UDP". Defaults to "TCP". - Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"` + // Optional: Defaults to "TCP". + Protocol Protocol `yaml:"protocol,omitempty" json:"protocol,omitempty"` // Optional: What host IP to bind the external port to. HostIP string `yaml:"hostIP,omitempty" json:"hostIP,omitempty"` } @@ -161,8 +171,6 @@ type ExecAction struct { // LivenessProbe describes a liveness probe to be examined to the container. // TODO: pass structured data to the actions, and document that data here. type LivenessProbe struct { - // Type of liveness probe. Current legal values "http", "tcp" - Type string `yaml:"type,omitempty" json:"type,omitempty"` // HTTPGetProbe parameters, required if Type == 'http' HTTPGet *HTTPGetAction `yaml:"httpGet,omitempty" json:"httpGet,omitempty"` // TCPSocketProbe parameter, required if Type == 'tcp' @@ -390,8 +398,8 @@ type Service struct { // Required. Port int `json:"port" yaml:"port"` - // Optional: Supports "TCP" and "UDP". Defaults to "TCP". - Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"` + // Optional: Defaults to "TCP". + Protocol Protocol `yaml:"protocol,omitempty" json:"protocol,omitempty"` // This service's labels. Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` @@ -456,12 +464,12 @@ func (*Binding) IsAnAPIObject() {} // import both. type Status struct { JSONBase `json:",inline" yaml:",inline"` - // One of: "success", "failure", "working" (for operations not yet completed) + // One of: "Success", "Failure", "Working" (for operations not yet completed) Status string `json:"status,omitempty" yaml:"status,omitempty"` // A human-readable description of the status of this operation. Message string `json:"message,omitempty" yaml:"message,omitempty"` // A machine-readable description of why this operation is in the - // "failure" or "working" status. If this value is empty there + // "Failure" or "Working" status. If this value is empty there // is no information available. A Reason clarifies an HTTP status // code but does not override it. Reason StatusReason `json:"reason,omitempty" yaml:"reason,omitempty"` @@ -496,9 +504,9 @@ type StatusDetails struct { // Values of Status.Status const ( - StatusSuccess = "success" - StatusFailure = "failure" - StatusWorking = "working" + StatusSuccess = "Success" + StatusFailure = "Failure" + StatusWorking = "Working" ) // StatusReason is an enumeration of possible failure causes. Each StatusReason @@ -523,7 +531,7 @@ const ( // "Location" - HTTP header populated with a URL that can retrieved the final // status of this operation. // Status code 202 - StatusReasonWorking StatusReason = "working" + StatusReasonWorking StatusReason = "Working" // StatusReasonNotFound means one or more resources required for this operation // could not be found. @@ -533,21 +541,21 @@ const ( // resource. // "id" string - the identifier of the missing resource // Status code 404 - StatusReasonNotFound StatusReason = "not_found" + StatusReasonNotFound StatusReason = "NotFound" // StatusReasonAlreadyExists means the resource you are creating already exists. // Details (optional): // "kind" string - the kind attribute of the conflicting resource // "id" string - the identifier of the conflicting resource // Status code 409 - StatusReasonAlreadyExists StatusReason = "already_exists" + StatusReasonAlreadyExists StatusReason = "AlreadyExists" // StatusReasonConflict means the requested update operation cannot be completed // due to a conflict in the operation. The client may need to alter the request. // Each resource may define custom details that indicate the nature of the // conflict. // Status code 409 - StatusReasonConflict StatusReason = "conflict" + StatusReasonConflict StatusReason = "Conflict" // StatusReasonInvalid means the requested create or update operation cannot be // completed due to invalid data provided as part of the request. The client may @@ -560,7 +568,7 @@ const ( // provided resource that was invalid. The code, message, and // field attributes will be set. // Status code 422 - StatusReasonInvalid StatusReason = "invalid" + StatusReasonInvalid StatusReason = "Invalid" ) // StatusCause provides more information about an api.Status failure, including @@ -586,25 +594,25 @@ type StatusCause struct { // CauseType is a machine readable value providing more detail about what // occured in a status response. An operation may have multiple causes for a -// status (whether failure, success, or working). +// status (whether Failure, Success, or Working). type CauseType string const ( // CauseTypeFieldValueNotFound is used to report failure to find a requested value // (e.g. looking up an ID). - CauseTypeFieldValueNotFound CauseType = "fieldValueNotFound" + CauseTypeFieldValueNotFound CauseType = "FieldValueNotFound" // CauseTypeFieldValueInvalid is used to report required values that are not // provided (e.g. empty strings, null values, or empty arrays). - CauseTypeFieldValueRequired CauseType = "fieldValueRequired" + CauseTypeFieldValueRequired CauseType = "FieldValueRequired" // CauseTypeFieldValueDuplicate is used to report collisions of values that must be // unique (e.g. unique IDs). - CauseTypeFieldValueDuplicate CauseType = "fieldValueDuplicate" + CauseTypeFieldValueDuplicate CauseType = "FieldValueDuplicate" // CauseTypeFieldValueInvalid is used to report malformed values (e.g. failed regex // match). - CauseTypeFieldValueInvalid CauseType = "fieldValueInvalid" + CauseTypeFieldValueInvalid CauseType = "FieldValueInvalid" // CauseTypeFieldValueNotSupported is used to report valid (as per formatting rules) // values that can not be handled (e.g. an enumerated string). - CauseTypeFieldValueNotSupported CauseType = "fieldValueNotSupported" + CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported" ) // ServerOp is an operation delivered to API clients. diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 01d96b4c463..d209e3c8790 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -78,7 +78,7 @@ func validateHostDir(hostDir *api.HostDirectory) errs.ErrorList { return allErrs } -var supportedPortProtocols = util.NewStringSet("TCP", "UDP") +var supportedPortProtocols = util.NewStringSet(string(api.ProtocolTCP), string(api.ProtocolUDP)) func validatePorts(ports []api.Port) errs.ErrorList { allErrs := errs.ErrorList{} @@ -106,7 +106,7 @@ func validatePorts(ports []api.Port) errs.ErrorList { } if len(port.Protocol) == 0 { port.Protocol = "TCP" - } else if !supportedPortProtocols.Has(strings.ToUpper(port.Protocol)) { + } else if !supportedPortProtocols.Has(strings.ToUpper(string(port.Protocol))) { pErrs = append(pErrs, errs.NewFieldNotSupported("protocol", port.Protocol)) } allErrs = append(allErrs, pErrs.PrefixIndex(i)...) @@ -330,7 +330,7 @@ func ValidateService(service *api.Service) errs.ErrorList { } if len(service.Protocol) == 0 { service.Protocol = "TCP" - } else if !supportedPortProtocols.Has(strings.ToUpper(service.Protocol)) { + } else if !supportedPortProtocols.Has(strings.ToUpper(string(service.Protocol))) { allErrs = append(allErrs, errs.NewFieldNotSupported("protocol", service.Protocol)) } if labels.Set(service.Selector).AsSelector().Empty() { diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index c5d713a14a5..79fc5dfc4f4 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -706,7 +706,7 @@ func TestAsyncCreateError(t *testing.T) { expectedStatus := &api.Status{ Status: api.StatusFailure, Code: http.StatusConflict, - Reason: "already_exists", + Reason: "AlreadyExists", Message: expectedErr.Error(), Details: &api.StatusDetails{ Kind: "foo", diff --git a/pkg/apiserver/errors_test.go b/pkg/apiserver/errors_test.go index 13b3ae4ad83..62f6ed1311e 100644 --- a/pkg/apiserver/errors_test.go +++ b/pkg/apiserver/errors_test.go @@ -39,7 +39,7 @@ func TestErrorsToAPIStatus(t *testing.T) { errors.NewAlreadyExists("foo", "bar"): { Status: api.StatusFailure, Code: http.StatusConflict, - Reason: "already_exists", + Reason: "AlreadyExists", Message: "foo \"bar\" already exists", Details: &api.StatusDetails{ Kind: "foo", @@ -49,7 +49,7 @@ func TestErrorsToAPIStatus(t *testing.T) { errors.NewConflict("foo", "bar", stderrs.New("failure")): { Status: api.StatusFailure, Code: http.StatusConflict, - Reason: "conflict", + Reason: "Conflict", Message: "foo \"bar\" cannot be updated: failure", Details: &api.StatusDetails{ Kind: "foo", diff --git a/pkg/health/exec.go b/pkg/health/exec.go index efcfec91507..ba95f2f3ce2 100644 --- a/pkg/health/exec.go +++ b/pkg/health/exec.go @@ -57,3 +57,7 @@ func (e *ExecHealthChecker) HealthCheck(podFullName string, currentState api.Pod } return Healthy, nil } + +func (e *ExecHealthChecker) CanCheck(probe *api.LivenessProbe) bool { + return probe.Exec != nil +} diff --git a/pkg/health/exec_test.go b/pkg/health/exec_test.go index 44727779a96..28e1a9ccaf3 100644 --- a/pkg/health/exec_test.go +++ b/pkg/health/exec_test.go @@ -49,22 +49,19 @@ func TestExec(t *testing.T) { checker := ExecHealthChecker{&fake} tests := []healthCheckTest{ // Missing parameters - {Unknown, &api.LivenessProbe{Type: "exec"}, true, nil, nil}, + {Unknown, &api.LivenessProbe{}, true, nil, nil}, // Ok {Healthy, &api.LivenessProbe{ - Type: "exec", Exec: &api.ExecAction{Command: []string{"ls", "-l"}}, }, false, []byte("OK"), nil}, // Run returns error {Unknown, &api.LivenessProbe{ - Type: "exec", Exec: &api.ExecAction{ Command: []string{"ls", "-l"}, }, }, true, []byte("OK, NOT"), fmt.Errorf("test error")}, // Command error {Unhealthy, &api.LivenessProbe{ - Type: "exec", Exec: &api.ExecAction{ Command: []string{"ls", "-l"}, }, diff --git a/pkg/health/health.go b/pkg/health/health.go index 80c0b604373..c978b00b7ba 100644 --- a/pkg/health/health.go +++ b/pkg/health/health.go @@ -36,54 +36,61 @@ const ( // HealthChecker defines an abstract interface for checking container health. type HealthChecker interface { HealthCheck(podFullName string, currentState api.PodState, container api.Container) (Status, error) + CanCheck(probe *api.LivenessProbe) bool } -// protects checkers +// protects allCheckers var checkerLock = sync.Mutex{} -var checkers = map[string]HealthChecker{} +var allCheckers = []HealthChecker{} // AddHealthChecker adds a health checker to the list of known HealthChecker objects. // Any subsequent call to NewHealthChecker will know about this HealthChecker. -// Panics if 'key' is already present. -func AddHealthChecker(key string, checker HealthChecker) { +func AddHealthChecker(checker HealthChecker) { checkerLock.Lock() defer checkerLock.Unlock() - if _, found := checkers[key]; found { - glog.Fatalf("HealthChecker already defined for key %s.", key) - } - checkers[key] = checker + allCheckers = append(allCheckers, checker) } // NewHealthChecker creates a new HealthChecker which supports multiple types of liveness probes. func NewHealthChecker() HealthChecker { checkerLock.Lock() defer checkerLock.Unlock() - input := map[string]HealthChecker{} - for key, value := range checkers { - input[key] = value - } return &muxHealthChecker{ - checkers: input, + checkers: append([]HealthChecker{}, allCheckers...), } } // muxHealthChecker bundles multiple implementations of HealthChecker of different types. type muxHealthChecker struct { - checkers map[string]HealthChecker + // Given a LivenessProbe, cycle through each known checker and see if it supports + // the specific kind of probe (by returning non-nil). + checkers []HealthChecker +} + +func (m *muxHealthChecker) findCheckerFor(probe *api.LivenessProbe) HealthChecker { + for i := range m.checkers { + if m.checkers[i].CanCheck(probe) { + return m.checkers[i] + } + } + return nil } // HealthCheck delegates the health-checking of the container to one of the bundled implementations. -// It chooses an implementation according to container.LivenessProbe.Type. -// If there is no matching health checker it returns Unknown, nil. +// If there is no health checker that can check container it returns Unknown, nil. func (m *muxHealthChecker) HealthCheck(podFullName string, currentState api.PodState, container api.Container) (Status, error) { - checker, ok := m.checkers[container.LivenessProbe.Type] - if !ok || checker == nil { - glog.Warningf("Failed to find health checker for %s %s", container.Name, container.LivenessProbe.Type) + checker := m.findCheckerFor(container.LivenessProbe) + if checker == nil { + glog.Warningf("Failed to find health checker for %s %+v", container.Name, container.LivenessProbe) return Unknown, nil } return checker.HealthCheck(podFullName, currentState, container) } +func (m *muxHealthChecker) CanCheck(probe *api.LivenessProbe) bool { + return m.findCheckerFor(probe) != nil +} + // findPortByName is a helper function to look up a port in a container by name. // Returns the HostPort if found, -1 if not found. func findPortByName(container api.Container, portName string) int { diff --git a/pkg/health/health_test.go b/pkg/health/health_test.go index 246f869f114..6c08a90bde8 100644 --- a/pkg/health/health_test.go +++ b/pkg/health/health_test.go @@ -30,7 +30,7 @@ import ( const statusServerEarlyShutdown = -1 func TestHealthChecker(t *testing.T) { - AddHealthChecker("http", &HTTPHealthChecker{client: &http.Client{}}) + AddHealthChecker(&HTTPHealthChecker{client: &http.Client{}}) var healthCheckerTests = []struct { status int health Status @@ -64,7 +64,6 @@ func TestHealthChecker(t *testing.T) { Path: "/foo/bar", Host: host, }, - Type: "http", }, } hc := NewHealthChecker() @@ -100,19 +99,16 @@ func TestFindPortByName(t *testing.T) { func TestMuxHealthChecker(t *testing.T) { muxHealthCheckerTests := []struct { - health Status - probeType string + health Status }{ - {Healthy, "http"}, - {Unknown, "ftp"}, + // TODO: This test should run through a few different checker types. + {Healthy}, } mc := &muxHealthChecker{ - checkers: make(map[string]HealthChecker), + checkers: []HealthChecker{ + &HTTPHealthChecker{client: &http.Client{}}, + }, } - hc := &HTTPHealthChecker{ - client: &http.Client{}, - } - mc.checkers["http"] = hc for _, muxHealthCheckerTest := range muxHealthCheckerTests { tt := muxHealthCheckerTest ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -131,7 +127,6 @@ func TestMuxHealthChecker(t *testing.T) { HTTPGet: &api.HTTPGetAction{}, }, } - container.LivenessProbe.Type = tt.probeType container.LivenessProbe.HTTPGet.Port = util.NewIntOrStringFromString(port) container.LivenessProbe.HTTPGet.Host = host health, err := mc.HealthCheck("test", api.PodState{}, container) diff --git a/pkg/health/http.go b/pkg/health/http.go index 8ee739dc59d..18ced9faaf1 100644 --- a/pkg/health/http.go +++ b/pkg/health/http.go @@ -105,3 +105,7 @@ func (h *HTTPHealthChecker) HealthCheck(podFullName string, currentState api.Pod } return DoHTTPCheck(formatURL(host, port, path), h.client) } + +func (h *HTTPHealthChecker) CanCheck(probe *api.LivenessProbe) bool { + return probe.HTTPGet != nil +} diff --git a/pkg/health/http_test.go b/pkg/health/http_test.go index d2067231bd6..5fb57217ce8 100644 --- a/pkg/health/http_test.go +++ b/pkg/health/http_test.go @@ -51,7 +51,6 @@ func TestGetURLParts(t *testing.T) { Ports: []api.Port{{Name: "found", HostPort: 93}}, LivenessProbe: &api.LivenessProbe{ HTTPGet: test.probe, - Type: "http", }, } host, port, path, err := getURLParts(state, container) @@ -117,7 +116,6 @@ func TestHTTPHealthChecker(t *testing.T) { container := api.Container{ LivenessProbe: &api.LivenessProbe{ HTTPGet: test.probe, - Type: "http", }, } params := container.LivenessProbe.HTTPGet diff --git a/pkg/health/tcp.go b/pkg/health/tcp.go index 1bcfb15f30e..7253c7597d0 100644 --- a/pkg/health/tcp.go +++ b/pkg/health/tcp.go @@ -81,3 +81,7 @@ func (t *TCPHealthChecker) HealthCheck(podFullName string, currentState api.PodS } return DoTCPCheck(net.JoinHostPort(host, strconv.Itoa(port))) } + +func (t *TCPHealthChecker) CanCheck(probe *api.LivenessProbe) bool { + return probe.TCPSocket != nil +} diff --git a/pkg/health/tcp_test.go b/pkg/health/tcp_test.go index bd17328a11b..2689ae615d1 100644 --- a/pkg/health/tcp_test.go +++ b/pkg/health/tcp_test.go @@ -49,7 +49,6 @@ func TestGetTCPAddrParts(t *testing.T) { Ports: []api.Port{{Name: "found", HostPort: 93}}, LivenessProbe: &api.LivenessProbe{ TCPSocket: test.probe, - Type: "tcp", }, } host, port, err := getTCPAddrParts(state, container) @@ -95,7 +94,6 @@ func TestTcpHealthChecker(t *testing.T) { container := api.Container{ LivenessProbe: &api.LivenessProbe{ TCPSocket: test.probe, - Type: "tcp", }, } params := container.LivenessProbe.TCPSocket diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index bee489f3d55..5ad944dc245 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -222,7 +222,7 @@ func makePortsAndBindings(container *api.Container) (map[docker.Port]struct{}, m // Some of this port stuff is under-documented voodoo. // See http://stackoverflow.com/questions/20428302/binding-a-port-to-a-host-interface-using-the-rest-api var protocol string - switch strings.ToUpper(port.Protocol) { + switch strings.ToUpper(string(port.Protocol)) { case "UDP": protocol = "/udp" case "TCP": diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 5509bf7b62c..e9ebfbf0307 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -457,6 +457,10 @@ func (f *FalseHealthChecker) HealthCheck(podFullName string, state api.PodState, return health.Unhealthy, nil } +func (f *FalseHealthChecker) CanCheck(probe *api.LivenessProbe) bool { + return true +} + func TestSyncPodBadHash(t *testing.T) { kubelet, _, fakeDocker := newTestKubelet(t) kubelet.healthChecker = &FalseHealthChecker{} @@ -522,8 +526,7 @@ func TestSyncPodUnhealthy(t *testing.T) { Containers: []api.Container{ {Name: "bar", LivenessProbe: &api.LivenessProbe{ - // Always returns healthy == false - Type: "false", + // Always returns healthy == false }, }, }, diff --git a/pkg/proxy/proxier.go b/pkg/proxy/proxier.go index ecdf32cf425..339423676fa 100644 --- a/pkg/proxy/proxier.go +++ b/pkg/proxy/proxier.go @@ -32,7 +32,7 @@ import ( type serviceInfo struct { port int - protocol string + protocol api.Protocol socket proxySocket timeout time.Duration mu sync.Mutex // protects active @@ -276,8 +276,8 @@ func logTimeout(err error) bool { return false } -func newProxySocket(protocol string, host string, port int) (proxySocket, error) { - switch strings.ToUpper(protocol) { +func newProxySocket(protocol api.Protocol, host string, port int) (proxySocket, error) { + switch strings.ToUpper(string(protocol)) { case "TCP": listener, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port))) if err != nil { @@ -350,7 +350,7 @@ func (proxier *Proxier) setServiceInfo(service string, info *serviceInfo) { // addServiceOnUnusedPort starts listening for a new service, returning the // port it's using. For testing on a system with unknown ports used. The timeout only applies to UDP // connections, for now. -func (proxier *Proxier) addServiceOnUnusedPort(service, protocol string, timeout time.Duration) (string, error) { +func (proxier *Proxier) addServiceOnUnusedPort(service string, protocol api.Protocol, timeout time.Duration) (string, error) { sock, err := newProxySocket(protocol, proxier.address, 0) if err != nil { return "", err diff --git a/pkg/registry/service/rest.go b/pkg/registry/service/rest.go index 635533ff66e..0394ece8cc5 100644 --- a/pkg/registry/service/rest.go +++ b/pkg/registry/service/rest.go @@ -222,9 +222,9 @@ func makeEnvVariableName(str string) string { func makeLinkVariables(service api.Service, machine string) []api.EnvVar { prefix := makeEnvVariableName(service.ID) - protocol := "TCP" + protocol := string(api.ProtocolTCP) if service.Protocol != "" { - protocol = service.Protocol + protocol = string(service.Protocol) } portPrefix := fmt.Sprintf("%s_PORT_%d_%s", prefix, service.Port, strings.ToUpper(protocol)) return []api.EnvVar{