Merge pull request #7101 from liggitt/service_account

ServiceAccounts
This commit is contained in:
Nikhil Jindal
2015-05-12 10:23:41 -07:00
79 changed files with 5907 additions and 24 deletions

View File

@@ -51,6 +51,8 @@ func init() {
&ResourceQuotaList{},
&Namespace{},
&NamespaceList{},
&ServiceAccount{},
&ServiceAccountList{},
&Secret{},
&SecretList{},
&PersistentVolume{},
@@ -98,6 +100,8 @@ func (*ResourceQuota) IsAnAPIObject() {}
func (*ResourceQuotaList) IsAnAPIObject() {}
func (*Namespace) IsAnAPIObject() {}
func (*NamespaceList) IsAnAPIObject() {}
func (*ServiceAccount) IsAnAPIObject() {}
func (*ServiceAccountList) IsAnAPIObject() {}
func (*Secret) IsAnAPIObject() {}
func (*SecretList) IsAnAPIObject() {}
func (*PersistentVolume) IsAnAPIObject() {}

View File

@@ -814,6 +814,10 @@ type PodSpec struct {
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// ServiceAccount is the name of the ServiceAccount to use to run this pod
// The pod will be allowed to use secrets referenced by the ServiceAccount
ServiceAccount string `json:"serviceAccount"`
// Host is a request to schedule this pod onto a specific host. If it is non-empty,
// the the scheduler simply schedules this pod onto that host, assuming that it fits
// resource requirements.
@@ -1035,6 +1039,26 @@ type Service struct {
Status ServiceStatus `json:"status,omitempty"`
}
// ServiceAccount binds together:
// * a name, understood by users, and perhaps by peripheral systems, for an identity
// * a principal that can be authenticated and authorized
// * a set of secrets
type ServiceAccount struct {
TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty"`
// Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount
Secrets []ObjectReference `json:"secrets"`
}
// ServiceAccountList is a list of ServiceAccount objects
type ServiceAccountList struct {
TypeMeta `json:",inline"`
ListMeta `json:"metadata,omitempty"`
Items []ServiceAccount `json:"items"`
}
// Endpoints is a collection of endpoints that implement the actual service. Example:
// Name: "mysvc",
// Subsets: [
@@ -1805,7 +1829,25 @@ const MaxSecretSize = 1 * 1024 * 1024
type SecretType string
const (
SecretTypeOpaque SecretType = "Opaque" // Default; arbitrary user-defined data
// SecretTypeOpaque is the default; arbitrary user-defined data
SecretTypeOpaque SecretType = "Opaque"
// SecretTypeServiceAccountToken contains a token that identifies a service account to the API
//
// Required fields:
// - Secret.Annotations["kubernetes.io/service-account.name"] - the name of the ServiceAccount the token identifies
// - Secret.Annotations["kubernetes.io/service-account.uid"] - the UID of the ServiceAccount the token identifies
// - Secret.Data["token"] - a token that identifies the service account to the API
SecretTypeServiceAccountToken SecretType = "kubernetes.io/service-account-token"
// ServiceAccountNameKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountNameKey = "kubernetes.io/service-account.name"
// ServiceAccountUIDKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountUIDKey = "kubernetes.io/service-account.uid"
// ServiceAccountTokenKey is the key of the required data for SecretTypeServiceAccountToken secrets
ServiceAccountTokenKey = "token"
// ServiceAccountKubeconfigKey is the key of the optional kubeconfig data for SecretTypeServiceAccountToken secrets
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
)
type SecretList struct {

View File

@@ -1880,6 +1880,7 @@ func init() {
out.NodeSelector[key] = val
}
}
out.ServiceAccount = in.ServiceAccount
out.Host = in.Host
out.HostNetwork = in.HostNetwork
return nil
@@ -1913,6 +1914,7 @@ func init() {
out.NodeSelector[key] = val
}
}
out.ServiceAccount = in.ServiceAccount
out.Host = in.Host
out.HostNetwork = in.HostNetwork
return nil
@@ -2477,6 +2479,74 @@ func init() {
}
return nil
},
func(in *ServiceAccount, out *newer.ServiceAccount, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
}
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if in.Secrets != nil {
out.Secrets = make([]newer.ObjectReference, len(in.Secrets))
for i := range in.Secrets {
if err := s.Convert(&in.Secrets[i], &out.Secrets[i], 0); err != nil {
return err
}
}
}
return nil
},
func(in *newer.ServiceAccount, out *ServiceAccount, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
}
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if in.Secrets != nil {
out.Secrets = make([]ObjectReference, len(in.Secrets))
for i := range in.Secrets {
if err := s.Convert(&in.Secrets[i], &out.Secrets[i], 0); err != nil {
return err
}
}
}
return nil
},
func(in *ServiceAccountList, out *newer.ServiceAccountList, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
}
if err := s.Convert(&in.ListMeta, &out.ListMeta, 0); err != nil {
return err
}
if in.Items != nil {
out.Items = make([]newer.ServiceAccount, len(in.Items))
for i := range in.Items {
if err := s.Convert(&in.Items[i], &out.Items[i], 0); err != nil {
return err
}
}
}
return nil
},
func(in *newer.ServiceAccountList, out *ServiceAccountList, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
}
if err := s.Convert(&in.ListMeta, &out.ListMeta, 0); err != nil {
return err
}
if in.Items != nil {
out.Items = make([]ServiceAccount, len(in.Items))
for i := range in.Items {
if err := s.Convert(&in.Items[i], &out.Items[i], 0); err != nil {
return err
}
}
}
return nil
},
func(in *ServiceList, out *newer.ServiceList, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err

View File

@@ -52,6 +52,8 @@ func init() {
&NamespaceList{},
&Secret{},
&SecretList{},
&ServiceAccount{},
&ServiceAccountList{},
&PersistentVolume{},
&PersistentVolumeList{},
&PersistentVolumeClaim{},
@@ -97,6 +99,8 @@ func (*Namespace) IsAnAPIObject() {}
func (*NamespaceList) IsAnAPIObject() {}
func (*Secret) IsAnAPIObject() {}
func (*SecretList) IsAnAPIObject() {}
func (*ServiceAccount) IsAnAPIObject() {}
func (*ServiceAccountList) IsAnAPIObject() {}
func (*PersistentVolume) IsAnAPIObject() {}
func (*PersistentVolumeList) IsAnAPIObject() {}
func (*PersistentVolumeClaim) IsAnAPIObject() {}

View File

@@ -816,6 +816,9 @@ type PodSpec struct {
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty" description:"selector which must match a node's labels for the pod to be scheduled on that node"`
// ServiceAccount is the name of the ServiceAccount to use to run this pod
ServiceAccount string `json:"serviceAccount" description:"name of the ServiceAccount to use to run this pod"`
// Host is a request to schedule this pod onto a specific host. If it is non-empty,
// the the scheduler simply schedules this pod onto that host, assuming that it fits
// resource requirements.
@@ -1036,6 +1039,26 @@ type ServiceList struct {
Items []Service `json:"items" description:"list of services"`
}
// ServiceAccount binds together:
// * a name, understood by users, and perhaps by peripheral systems, for an identity
// * a principal that can be authenticated and authorized
// * a set of secrets
type ServiceAccount struct {
TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty" description:"standard object metadata; see http://docs.k8s.io/api-conventions.md#metadata"`
// Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount
Secrets []ObjectReference `json:"secrets" description:"list of secrets that can be used by pods running as this service account" patchStrategy:"merge" patchMergeKey:"name"`
}
// ServiceAccountList is a list of ServiceAccount objects
type ServiceAccountList struct {
TypeMeta `json:",inline"`
ListMeta `json:"metadata,omitempty" description:"standard list metadata; see http://docs.k8s.io/api-conventions.md#metadata"`
Items []ServiceAccount `json:"items" description:"list of ServiceAccounts"`
}
// Endpoints is a collection of endpoints that implement the actual service. Example:
// Name: "mysvc",
// Subsets: [
@@ -1708,7 +1731,23 @@ const MaxSecretSize = 1 * 1024 * 1024
type SecretType string
const (
SecretTypeOpaque SecretType = "Opaque" // Default; arbitrary user-defined data
// SecretTypeOpaque is the default; arbitrary user-defined data
SecretTypeOpaque SecretType = "Opaque"
// SecretTypeServiceAccountToken contains a token that identifies a service account to the API
//
// Required fields:
// - Secret.Annotations["kubernetes.io/service-account.name"] - the name of the ServiceAccount the token identifies
// - Secret.Annotations["kubernetes.io/service-account.uid"] - the UID of the ServiceAccount the token identifies
// - Secret.Data["token"] - a token that identifies the service account to the API
SecretTypeServiceAccountToken SecretType = "kubernetes.io/service-account-token"
// ServiceAccountNameKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountNameKey = "kubernetes.io/service-account.name"
// ServiceAccountUIDKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountUIDKey = "kubernetes.io/service-account.uid"
// ServiceAccountTokenKey is the key of the required data for SecretTypeServiceAccountToken secrets
ServiceAccountTokenKey = "token"
)
type SecretList struct {

View File

@@ -377,6 +377,7 @@ func init() {
}
out.DesiredState.Host = in.Spec.Host
out.CurrentState.Host = in.Spec.Host
out.ServiceAccount = in.Spec.ServiceAccount
if err := s.Convert(&in.Status, &out.CurrentState, 0); err != nil {
return err
}
@@ -399,6 +400,7 @@ func init() {
return err
}
out.Spec.Host = in.DesiredState.Host
out.Spec.ServiceAccount = in.ServiceAccount
if err := s.Convert(&in.CurrentState, &out.Status, 0); err != nil {
return err
}
@@ -503,6 +505,7 @@ func init() {
return err
}
out.DesiredState.Host = in.Spec.Host
out.ServiceAccount = in.Spec.ServiceAccount
if err := s.Convert(&in.Spec.NodeSelector, &out.NodeSelector, 0); err != nil {
return err
}
@@ -519,6 +522,7 @@ func init() {
return err
}
out.Spec.Host = in.DesiredState.Host
out.Spec.ServiceAccount = in.ServiceAccount
if err := s.Convert(&in.NodeSelector, &out.Spec.NodeSelector, 0); err != nil {
return err
}
@@ -1685,4 +1689,17 @@ func init() {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "ServiceAccount",
func(label, value string) (string, string, error) {
switch label {
case "name":
return "metadata.name", value, nil
default:
return "", "", fmt.Errorf("field label not supported: %s", label)
}
})
if err != nil {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
}

View File

@@ -59,6 +59,8 @@ func init() {
&NamespaceList{},
&Secret{},
&SecretList{},
&ServiceAccount{},
&ServiceAccountList{},
&PersistentVolume{},
&PersistentVolumeList{},
&PersistentVolumeClaim{},
@@ -105,6 +107,8 @@ func (*Namespace) IsAnAPIObject() {}
func (*NamespaceList) IsAnAPIObject() {}
func (*Secret) IsAnAPIObject() {}
func (*SecretList) IsAnAPIObject() {}
func (*ServiceAccount) IsAnAPIObject() {}
func (*ServiceAccountList) IsAnAPIObject() {}
func (*PersistentVolume) IsAnAPIObject() {}
func (*PersistentVolumeList) IsAnAPIObject() {}
func (*PersistentVolumeClaim) IsAnAPIObject() {}

View File

@@ -755,6 +755,8 @@ type Pod struct {
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize pods; may match selectors of replication controllers and services"`
DesiredState PodState `json:"desiredState,omitempty" description:"specification of the desired state of the pod"`
CurrentState PodState `json:"currentState,omitempty" description:"current state of the pod; populated by the system, read-only"`
// ServiceAccount is the name of the ServiceAccount to use to run this pod
ServiceAccount string `json:"serviceAccount,omitempty" description:"the name of the ServiceAccount to use to run this pod"`
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty" description:"selector which must match a node's labels for the pod to be scheduled on that node"`
}
@@ -782,10 +784,11 @@ type ReplicationController struct {
// PodTemplate holds the information used for creating pods.
type PodTemplate struct {
DesiredState PodState `json:"desiredState,omitempty" description:"specification of the desired state of pods created from this template"`
NodeSelector map[string]string `json:"nodeSelector,omitempty" description:"a selector which must be true for the pod to fit on a node"`
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize the pods created from the template; must match the selector of the replication controller to which the template belongs; may match selectors of services"`
Annotations map[string]string `json:"annotations,omitempty" description:"map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about pods created from the template"`
DesiredState PodState `json:"desiredState,omitempty" description:"specification of the desired state of pods created from this template"`
ServiceAccount string `json:"serviceAccount,omitempty" description:"the name of the ServiceAccount to use to run this pod"`
NodeSelector map[string]string `json:"nodeSelector,omitempty" description:"a selector which must be true for the pod to fit on a node"`
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize the pods created from the template; must match the selector of the replication controller to which the template belongs; may match selectors of services"`
Annotations map[string]string `json:"annotations,omitempty" description:"map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about pods created from the template"`
}
// Session Affinity Type string
@@ -884,6 +887,24 @@ type ServicePort struct {
ContainerPort util.IntOrString `json:"containerPort" description:"the port to access on the containers belonging to pods targeted by the service; defaults to the service port"`
}
// ServiceAccount binds together:
// * a name, understood by users, and perhaps by peripheral systems, for an identity
// * a principal that can be authenticated and authorized
// * a set of secrets
type ServiceAccount struct {
TypeMeta `json:",inline"`
// Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount
Secrets []ObjectReference `json:"secrets" description:"list of secrets that can be used by pods running as this service account" patchStrategy:"merge" patchMergeKey:"name"`
}
// ServiceAccountList is a list of ServiceAccount objects
type ServiceAccountList struct {
TypeMeta `json:",inline"`
Items []ServiceAccount `json:"items" description:"list of ServiceAccounts"`
}
// EndpointObjectReference is a reference to an object exposing the endpoint
type EndpointObjectReference struct {
Endpoint string `json:"endpoint" description:"endpoint exposed by the referenced object"`
@@ -1617,7 +1638,23 @@ const MaxSecretSize = 1 * 1024 * 1024
type SecretType string
const (
SecretTypeOpaque SecretType = "Opaque" // Default; arbitrary user-defined data
// SecretTypeOpaque is the default; arbitrary user-defined data
SecretTypeOpaque SecretType = "Opaque"
// SecretTypeServiceAccountToken contains a token that identifies a service account to the API
//
// Required fields:
// - Secret.Annotations["kubernetes.io/service-account.name"] - the name of the ServiceAccount the token identifies
// - Secret.Annotations["kubernetes.io/service-account.uid"] - the UID of the ServiceAccount the token identifies
// - Secret.Data["token"] - a token that identifies the service account to the API
SecretTypeServiceAccountToken SecretType = "kubernetes.io/service-account-token"
// ServiceAccountNameKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountNameKey = "kubernetes.io/service-account.name"
// ServiceAccountUIDKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountUIDKey = "kubernetes.io/service-account.uid"
// ServiceAccountTokenKey is the key of the required data for SecretTypeServiceAccountToken secrets
ServiceAccountTokenKey = "token"
)
type SecretList struct {

View File

@@ -180,6 +180,7 @@ func init() {
}
out.DesiredState.Host = in.Spec.Host
out.CurrentState.Host = in.Spec.Host
out.ServiceAccount = in.Spec.ServiceAccount
if err := s.Convert(&in.Status, &out.CurrentState, 0); err != nil {
return err
}
@@ -201,6 +202,7 @@ func init() {
if err := s.Convert(&in.DesiredState.Manifest, &out.Spec, 0); err != nil {
return err
}
out.Spec.ServiceAccount = in.ServiceAccount
out.Spec.Host = in.DesiredState.Host
if err := s.Convert(&in.CurrentState, &out.Status, 0); err != nil {
return err
@@ -282,6 +284,7 @@ func init() {
return err
}
out.DesiredState.Host = in.Spec.Host
out.ServiceAccount = in.Spec.ServiceAccount
if err := s.Convert(&in.Spec.NodeSelector, &out.NodeSelector, 0); err != nil {
return err
}
@@ -298,6 +301,7 @@ func init() {
return err
}
out.Spec.Host = in.DesiredState.Host
out.Spec.ServiceAccount = in.ServiceAccount
if err := s.Convert(&in.NodeSelector, &out.Spec.NodeSelector, 0); err != nil {
return err
}
@@ -1601,4 +1605,17 @@ func init() {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta2", "ServiceAccount",
func(label, value string) (string, string, error) {
switch label {
case "name":
return "metadata.name", value, nil
default:
return "", "", fmt.Errorf("field label not supported: %s", label)
}
})
if err != nil {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
}

View File

@@ -59,6 +59,8 @@ func init() {
&NamespaceList{},
&Secret{},
&SecretList{},
&ServiceAccount{},
&ServiceAccountList{},
&PersistentVolume{},
&PersistentVolumeList{},
&PersistentVolumeClaim{},
@@ -105,6 +107,8 @@ func (*Namespace) IsAnAPIObject() {}
func (*NamespaceList) IsAnAPIObject() {}
func (*Secret) IsAnAPIObject() {}
func (*SecretList) IsAnAPIObject() {}
func (*ServiceAccount) IsAnAPIObject() {}
func (*ServiceAccountList) IsAnAPIObject() {}
func (*PersistentVolume) IsAnAPIObject() {}
func (*PersistentVolumeList) IsAnAPIObject() {}
func (*PersistentVolumeClaim) IsAnAPIObject() {}

View File

@@ -756,6 +756,8 @@ type Pod struct {
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize pods; may match selectors of replication controllers and services"`
DesiredState PodState `json:"desiredState,omitempty" description:"specification of the desired state of the pod"`
CurrentState PodState `json:"currentState,omitempty" description:"current state of the pod; populated by the system, read-only"`
// ServiceAccount is the name of the ServiceAccount to use to run this pod
ServiceAccount string `json:"serviceAccount,omitempty" description:"the name of the ServiceAccount to use to run this pod"`
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty" description:"selector which must match a node's labels for the pod to be scheduled on that node"`
}
@@ -787,10 +789,11 @@ type ReplicationController struct {
//
// http://docs.k8s.io/replication-controller.md#pod-template
type PodTemplate struct {
DesiredState PodState `json:"desiredState,omitempty" description:"specification of the desired state of pods created from this template"`
NodeSelector map[string]string `json:"nodeSelector,omitempty" description:"a selector which must be true for the pod to fit on a node"`
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize the pods created from the template; must match the selector of the replication controller to which the template belongs; may match selectors of services"`
Annotations map[string]string `json:"annotations,omitempty" description:"map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about pods created from the template"`
DesiredState PodState `json:"desiredState,omitempty" description:"specification of the desired state of pods created from this template"`
ServiceAccount string `json:"serviceAccount,omitempty" description:"the name of the ServiceAccount to use to run this pod"`
NodeSelector map[string]string `json:"nodeSelector,omitempty" description:"a selector which must be true for the pod to fit on a node"`
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize the pods created from the template; must match the selector of the replication controller to which the template belongs; may match selectors of services"`
Annotations map[string]string `json:"annotations,omitempty" description:"map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about pods created from the template"`
}
// Session Affinity Type string
@@ -891,6 +894,24 @@ type ServicePort struct {
ContainerPort util.IntOrString `json:"containerPort" description:"the port to access on the containers belonging to pods targeted by the service; defaults to the service port"`
}
// ServiceAccount binds together:
// * a name, understood by users, and perhaps by peripheral systems, for an identity
// * a principal that can be authenticated and authorized
// * a set of secrets
type ServiceAccount struct {
TypeMeta `json:",inline"`
// Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount
Secrets []ObjectReference `json:"secrets" description:"list of secrets that can be used by pods running as this service account" patchStrategy:"merge" patchMergeKey:"name"`
}
// ServiceAccountList is a list of ServiceAccount objects
type ServiceAccountList struct {
TypeMeta `json:",inline"`
Items []ServiceAccount `json:"items" description:"list of ServiceAccounts"`
}
// EndpointObjectReference is a reference to an object exposing the endpoint
type EndpointObjectReference struct {
Endpoint string `json:"endpoint" description:"endpoint exposed by the referenced object"`
@@ -1692,7 +1713,23 @@ const MaxSecretSize = 1 * 1024 * 1024
type SecretType string
const (
SecretTypeOpaque SecretType = "Opaque" // Default; arbitrary user-defined data
// SecretTypeOpaque is the default; arbitrary user-defined data
SecretTypeOpaque SecretType = "Opaque"
// SecretTypeServiceAccountToken contains a token that identifies a service account to the API
//
// Required fields:
// - Secret.Annotations["kubernetes.io/service-account.name"] - the name of the ServiceAccount the token identifies
// - Secret.Annotations["kubernetes.io/service-account.uid"] - the UID of the ServiceAccount the token identifies
// - Secret.Data["token"] - a token that identifies the service account to the API
SecretTypeServiceAccountToken SecretType = "kubernetes.io/service-account-token"
// ServiceAccountNameKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountNameKey = "kubernetes.io/service-account.name"
// ServiceAccountUIDKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountUIDKey = "kubernetes.io/service-account.uid"
// ServiceAccountTokenKey is the key of the required data for SecretTypeServiceAccountToken secrets
ServiceAccountTokenKey = "token"
)
type SecretList struct {

View File

@@ -128,6 +128,19 @@ func init() {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta3", "ServiceAccount",
func(label, value string) (string, string, error) {
switch label {
case "metadata.name":
return label, value, nil
default:
return "", "", fmt.Errorf("field label not supported: %s", label)
}
})
if err != nil {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
}
func convert_v1beta3_Container_To_api_Container(in *Container, out *newer.Container, s conversion.Scope) error {

View File

@@ -2639,6 +2639,7 @@ func convert_v1beta3_PodSpec_To_api_PodSpec(in *PodSpec, out *newer.PodSpec, s c
} else {
out.NodeSelector = nil
}
out.ServiceAccount = in.ServiceAccount
out.Host = in.Host
out.HostNetwork = in.HostNetwork
return nil
@@ -2684,6 +2685,7 @@ func convert_api_PodSpec_To_v1beta3_PodSpec(in *newer.PodSpec, out *PodSpec, s c
} else {
out.NodeSelector = nil
}
out.ServiceAccount = in.ServiceAccount
out.Host = in.Host
out.HostNetwork = in.HostNetwork
return nil
@@ -3625,6 +3627,98 @@ func convert_api_Service_To_v1beta3_Service(in *newer.Service, out *Service, s c
return nil
}
func convert_v1beta3_ServiceAccount_To_api_ServiceAccount(in *ServiceAccount, out *newer.ServiceAccount, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*ServiceAccount))(in)
}
if err := convert_v1beta3_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
if err := convert_v1beta3_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
return err
}
if in.Secrets != nil {
out.Secrets = make([]newer.ObjectReference, len(in.Secrets))
for i := range in.Secrets {
if err := convert_v1beta3_ObjectReference_To_api_ObjectReference(&in.Secrets[i], &out.Secrets[i], s); err != nil {
return err
}
}
} else {
out.Secrets = nil
}
return nil
}
func convert_api_ServiceAccount_To_v1beta3_ServiceAccount(in *newer.ServiceAccount, out *ServiceAccount, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*newer.ServiceAccount))(in)
}
if err := convert_api_TypeMeta_To_v1beta3_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
if err := convert_api_ObjectMeta_To_v1beta3_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
return err
}
if in.Secrets != nil {
out.Secrets = make([]ObjectReference, len(in.Secrets))
for i := range in.Secrets {
if err := convert_api_ObjectReference_To_v1beta3_ObjectReference(&in.Secrets[i], &out.Secrets[i], s); err != nil {
return err
}
}
} else {
out.Secrets = nil
}
return nil
}
func convert_v1beta3_ServiceAccountList_To_api_ServiceAccountList(in *ServiceAccountList, out *newer.ServiceAccountList, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*ServiceAccountList))(in)
}
if err := convert_v1beta3_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
if err := convert_v1beta3_ListMeta_To_api_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
return err
}
if in.Items != nil {
out.Items = make([]newer.ServiceAccount, len(in.Items))
for i := range in.Items {
if err := convert_v1beta3_ServiceAccount_To_api_ServiceAccount(&in.Items[i], &out.Items[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func convert_api_ServiceAccountList_To_v1beta3_ServiceAccountList(in *newer.ServiceAccountList, out *ServiceAccountList, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*newer.ServiceAccountList))(in)
}
if err := convert_api_TypeMeta_To_v1beta3_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
if err := convert_api_ListMeta_To_v1beta3_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
return err
}
if in.Items != nil {
out.Items = make([]ServiceAccount, len(in.Items))
for i := range in.Items {
if err := convert_api_ServiceAccount_To_v1beta3_ServiceAccount(&in.Items[i], &out.Items[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func convert_v1beta3_ServiceList_To_api_ServiceList(in *ServiceList, out *newer.ServiceList, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*ServiceList))(in)
@@ -4244,6 +4338,8 @@ func init() {
convert_api_Secret_To_v1beta3_Secret,
convert_api_SecurityContext_To_v1beta3_SecurityContext,
convert_api_SerializedReference_To_v1beta3_SerializedReference,
convert_api_ServiceAccountList_To_v1beta3_ServiceAccountList,
convert_api_ServiceAccount_To_v1beta3_ServiceAccount,
convert_api_ServiceList_To_v1beta3_ServiceList,
convert_api_ServicePort_To_v1beta3_ServicePort,
convert_api_ServiceSpec_To_v1beta3_ServiceSpec,
@@ -4350,6 +4446,8 @@ func init() {
convert_v1beta3_Secret_To_api_Secret,
convert_v1beta3_SecurityContext_To_api_SecurityContext,
convert_v1beta3_SerializedReference_To_api_SerializedReference,
convert_v1beta3_ServiceAccountList_To_api_ServiceAccountList,
convert_v1beta3_ServiceAccount_To_api_ServiceAccount,
convert_v1beta3_ServiceList_To_api_ServiceList,
convert_v1beta3_ServicePort_To_api_ServicePort,
convert_v1beta3_ServiceSpec_To_api_ServiceSpec,

View File

@@ -52,6 +52,8 @@ func init() {
&NamespaceList{},
&Secret{},
&SecretList{},
&ServiceAccount{},
&ServiceAccountList{},
&PersistentVolume{},
&PersistentVolumeList{},
&PersistentVolumeClaim{},
@@ -97,6 +99,8 @@ func (*Namespace) IsAnAPIObject() {}
func (*NamespaceList) IsAnAPIObject() {}
func (*Secret) IsAnAPIObject() {}
func (*SecretList) IsAnAPIObject() {}
func (*ServiceAccount) IsAnAPIObject() {}
func (*ServiceAccountList) IsAnAPIObject() {}
func (*PersistentVolume) IsAnAPIObject() {}
func (*PersistentVolumeList) IsAnAPIObject() {}
func (*PersistentVolumeClaim) IsAnAPIObject() {}

View File

@@ -816,6 +816,9 @@ type PodSpec struct {
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty" description:"selector which must match a node's labels for the pod to be scheduled on that node"`
// ServiceAccount is the name of the ServiceAccount to use to run this pod
ServiceAccount string `json:"serviceAccount" description:"name of the ServiceAccount to use to run this pod"`
// Host is a request to schedule this pod onto a specific host. If it is non-empty,
// the the scheduler simply schedules this pod onto that host, assuming that it fits
// resource requirements.
@@ -1036,6 +1039,26 @@ type ServiceList struct {
Items []Service `json:"items" description:"list of services"`
}
// ServiceAccount binds together:
// * a name, understood by users, and perhaps by peripheral systems, for an identity
// * a principal that can be authenticated and authorized
// * a set of secrets
type ServiceAccount struct {
TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty" description:"standard object metadata; see http://docs.k8s.io/api-conventions.md#metadata"`
// Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount
Secrets []ObjectReference `json:"secrets" description:"list of secrets that can be used by pods running as this service account" patchStrategy:"merge" patchMergeKey:"name"`
}
// ServiceAccountList is a list of ServiceAccount objects
type ServiceAccountList struct {
TypeMeta `json:",inline"`
ListMeta `json:"metadata,omitempty" description:"standard list metadata; see http://docs.k8s.io/api-conventions.md#metadata"`
Items []ServiceAccount `json:"items" description:"list of ServiceAccounts"`
}
// Endpoints is a collection of endpoints that implement the actual service. Example:
// Name: "mysvc",
// Subsets: [
@@ -1708,7 +1731,23 @@ const MaxSecretSize = 1 * 1024 * 1024
type SecretType string
const (
SecretTypeOpaque SecretType = "Opaque" // Default; arbitrary user-defined data
// SecretTypeOpaque is the default; arbitrary user-defined data
SecretTypeOpaque SecretType = "Opaque"
// SecretTypeServiceAccountToken contains a token that identifies a service account to the API
//
// Required fields:
// - Secret.Annotations["kubernetes.io/service-account.name"] - the name of the ServiceAccount the token identifies
// - Secret.Annotations["kubernetes.io/service-account.uid"] - the UID of the ServiceAccount the token identifies
// - Secret.Data["token"] - a token that identifies the service account to the API
SecretTypeServiceAccountToken SecretType = "kubernetes.io/service-account-token"
// ServiceAccountNameKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountNameKey = "kubernetes.io/service-account.name"
// ServiceAccountUIDKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
ServiceAccountUIDKey = "kubernetes.io/service-account.uid"
// ServiceAccountTokenKey is the key of the required data for SecretTypeServiceAccountToken secrets
ServiceAccountTokenKey = "token"
)
type SecretList struct {

View File

@@ -153,6 +153,13 @@ func ValidateSecretName(name string, prefix bool) (bool, string) {
return nameIsDNSSubdomain(name, prefix)
}
// ValidateServiceAccountName can be used to check whether the given service account name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateServiceAccountName(name string, prefix bool) (bool, string) {
return nameIsDNSSubdomain(name, prefix)
}
// ValidateEndpointsName can be used to check whether the given endpoints name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
@@ -1227,6 +1234,21 @@ func ValidateLimitRange(limitRange *api.LimitRange) errs.ValidationErrorList {
return allErrs
}
// ValidateServiceAccount tests if required fields in the ServiceAccount are set.
func ValidateServiceAccount(serviceAccount *api.ServiceAccount) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{}
allErrs = append(allErrs, ValidateObjectMeta(&serviceAccount.ObjectMeta, true, ValidateServiceAccountName).Prefix("metadata")...)
return allErrs
}
// ValidateServiceAccountUpdate tests if required fields in the ServiceAccount are set.
func ValidateServiceAccountUpdate(oldServiceAccount, newServiceAccount *api.ServiceAccount) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{}
allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldServiceAccount.ObjectMeta, &newServiceAccount.ObjectMeta).Prefix("metadata")...)
allErrs = append(allErrs, ValidateServiceAccount(newServiceAccount)...)
return allErrs
}
// ValidateSecret tests if required fields in the Secret are set.
func ValidateSecret(secret *api.Secret) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{}
@@ -1246,6 +1268,12 @@ func ValidateSecret(secret *api.Secret) errs.ValidationErrorList {
}
switch secret.Type {
case api.SecretTypeServiceAccountToken:
// Only require Annotations[kubernetes.io/service-account.name]
// Additional fields (like Annotations[kubernetes.io/service-account.uid] and Data[token]) might be contributed later by a controller loop
if value := secret.Annotations[api.ServiceAccountNameKey]; len(value) == 0 {
allErrs = append(allErrs, errs.NewFieldRequired(fmt.Sprintf("metadata.annotations[%s]", api.ServiceAccountNameKey)))
}
case api.SecretTypeOpaque, "":
// no-op
default:

View File

@@ -2961,6 +2961,7 @@ func TestValidateNamespaceUpdate(t *testing.T) {
}
func TestValidateSecret(t *testing.T) {
// Opaque secret validation
validSecret := func() api.Secret {
return api.Secret{
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
@@ -2988,6 +2989,32 @@ func TestValidateSecret(t *testing.T) {
}
invalidKey.Data["a..b"] = []byte("whoops")
// kubernetes.io/service-account-token secret validation
validServiceAccountTokenSecret := func() api.Secret {
return api.Secret{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: "bar",
Annotations: map[string]string{
api.ServiceAccountNameKey: "foo",
},
},
Type: api.SecretTypeServiceAccountToken,
Data: map[string][]byte{
"data-1": []byte("bar"),
},
}
}
var (
emptyTokenAnnotation = validServiceAccountTokenSecret()
missingTokenAnnotation = validServiceAccountTokenSecret()
missingTokenAnnotations = validServiceAccountTokenSecret()
)
emptyTokenAnnotation.Annotations[api.ServiceAccountNameKey] = ""
delete(missingTokenAnnotation.Annotations, api.ServiceAccountNameKey)
missingTokenAnnotations.Annotations = nil
tests := map[string]struct {
secret api.Secret
valid bool
@@ -2999,6 +3026,11 @@ func TestValidateSecret(t *testing.T) {
"invalid namespace": {invalidNs, false},
"over max size": {overMaxSize, false},
"invalid key": {invalidKey, false},
"valid service-account-token secret": {validServiceAccountTokenSecret(), true},
"empty service-account-token annotation": {emptyTokenAnnotation, false},
"missing service-account-token annotation": {missingTokenAnnotation, false},
"missing service-account-token annotations": {missingTokenAnnotations, false},
}
for name, tc := range tests {