Add ServiceAccount API type, client

pull/6/head
Jordan Liggitt 2015-04-27 18:53:28 -04:00
parent 6e1e7dbb24
commit 53d55f4192
30 changed files with 1010 additions and 8 deletions

View File

@ -259,6 +259,7 @@ _kubectl_get()
must_have_one_noun+=("resourcequota")
must_have_one_noun+=("secret")
must_have_one_noun+=("service")
must_have_one_noun+=("serviceaccount")
}
_kubectl_describe()
@ -286,6 +287,7 @@ _kubectl_describe()
must_have_one_noun+=("resourcequota")
must_have_one_noun+=("secret")
must_have_one_noun+=("service")
must_have_one_noun+=("serviceaccount")
}
_kubectl_create()

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: [

View File

@ -1878,6 +1878,7 @@ func init() {
out.NodeSelector[key] = val
}
}
out.ServiceAccount = in.ServiceAccount
out.Host = in.Host
out.HostNetwork = in.HostNetwork
return nil
@ -1911,6 +1912,7 @@ func init() {
out.NodeSelector[key] = val
}
}
out.ServiceAccount = in.ServiceAccount
out.Host = in.Host
out.HostNetwork = in.HostNetwork
return nil
@ -2475,6 +2477,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: [

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"`

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"`

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

@ -2637,6 +2637,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
@ -2682,6 +2683,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
@ -3623,6 +3625,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)
@ -4242,6 +4336,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,
@ -4348,6 +4444,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: [

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.
@ -1225,6 +1232,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{}

View File

@ -39,6 +39,7 @@ type Interface interface {
EventNamespacer
LimitRangesNamespacer
ResourceQuotasNamespacer
ServiceAccountsNamespacer
SecretsNamespacer
NamespacesInterface
PersistentVolumesInterface
@ -77,6 +78,10 @@ func (c *Client) ResourceQuotas(namespace string) ResourceQuotaInterface {
return newResourceQuotas(c, namespace)
}
func (c *Client) ServiceAccounts(namespace string) ServiceAccountsInterface {
return newServiceAccounts(c, namespace)
}
func (c *Client) Secrets(namespace string) SecretsInterface {
return newSecrets(c, namespace)
}

View File

@ -310,6 +310,9 @@ var fieldMappings = versionToResourceToFieldMapping{
"secrets": clientFieldNameToAPIVersionFieldName{
SecretType: "type",
},
"serviceAccounts": clientFieldNameToAPIVersionFieldName{
ObjectNameField: "name",
},
},
"v1beta2": resourceTypeToFieldMapping{
"nodes": clientFieldNameToAPIVersionFieldName{
@ -326,6 +329,9 @@ var fieldMappings = versionToResourceToFieldMapping{
"secrets": clientFieldNameToAPIVersionFieldName{
SecretType: "type",
},
"serviceAccounts": clientFieldNameToAPIVersionFieldName{
ObjectNameField: "name",
},
},
"v1beta3": resourceTypeToFieldMapping{
"nodes": clientFieldNameToAPIVersionFieldName{
@ -342,6 +348,9 @@ var fieldMappings = versionToResourceToFieldMapping{
"secrets": clientFieldNameToAPIVersionFieldName{
SecretType: "type",
},
"serviceAccounts": clientFieldNameToAPIVersionFieldName{
ObjectNameField: "metadata.name",
},
},
}

View File

@ -0,0 +1,125 @@
/*
Copyright 2014 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 client
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
type ServiceAccountsNamespacer interface {
ServiceAccounts(namespace string) ServiceAccountsInterface
}
type ServiceAccountsInterface interface {
Create(serviceAccount *api.ServiceAccount) (*api.ServiceAccount, error)
Update(serviceAccount *api.ServiceAccount) (*api.ServiceAccount, error)
Delete(name string) error
List(label labels.Selector, field fields.Selector) (*api.ServiceAccountList, error)
Get(name string) (*api.ServiceAccount, error)
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
}
// serviceAccounts implements ServiceAccounts interface
type serviceAccounts struct {
client *Client
namespace string
}
// newServiceAccounts returns a new serviceAccounts object.
func newServiceAccounts(c *Client, ns string) ServiceAccountsInterface {
return &serviceAccounts{
client: c,
namespace: ns,
}
}
func (s *serviceAccounts) Create(serviceAccount *api.ServiceAccount) (*api.ServiceAccount, error) {
result := &api.ServiceAccount{}
err := s.client.Post().
Namespace(s.namespace).
Resource("serviceAccounts").
Body(serviceAccount).
Do().
Into(result)
return result, err
}
// List returns a list of serviceAccounts matching the selectors.
func (s *serviceAccounts) List(label labels.Selector, field fields.Selector) (*api.ServiceAccountList, error) {
result := &api.ServiceAccountList{}
err := s.client.Get().
Namespace(s.namespace).
Resource("serviceAccounts").
LabelsSelectorParam(label).
FieldsSelectorParam(field).
Do().
Into(result)
return result, err
}
// Get returns the given serviceAccount, or an error.
func (s *serviceAccounts) Get(name string) (*api.ServiceAccount, error) {
result := &api.ServiceAccount{}
err := s.client.Get().
Namespace(s.namespace).
Resource("serviceAccounts").
Name(name).
Do().
Into(result)
return result, err
}
// Watch starts watching for serviceAccounts matching the given selectors.
func (s *serviceAccounts) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
return s.client.Get().
Prefix("watch").
Namespace(s.namespace).
Resource("serviceAccounts").
Param("resourceVersion", resourceVersion).
LabelsSelectorParam(label).
FieldsSelectorParam(field).
Watch()
}
func (s *serviceAccounts) Delete(name string) error {
return s.client.Delete().
Namespace(s.namespace).
Resource("serviceAccounts").
Name(name).
Do().
Error()
}
func (s *serviceAccounts) Update(serviceAccount *api.ServiceAccount) (result *api.ServiceAccount, err error) {
result = &api.ServiceAccount{}
err = s.client.Put().
Namespace(s.namespace).
Resource("serviceAccounts").
Name(serviceAccount.Name).
Body(serviceAccount).
Do().
Into(result)
return
}

View File

@ -0,0 +1,61 @@
/*
Copyright 2014 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 testclient
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
// FakeServiceAccounts implements ServiceAccountsInterface. Meant to be embedded into a struct to get a default
// implementation. This makes faking out just the method you want to test easier.
type FakeServiceAccounts struct {
Fake *Fake
Namespace string
}
func (c *FakeServiceAccounts) List(labels labels.Selector, field fields.Selector) (*api.ServiceAccountList, error) {
obj, err := c.Fake.Invokes(FakeAction{Action: "list-serviceaccounts"}, &api.ServiceAccountList{})
return obj.(*api.ServiceAccountList), err
}
func (c *FakeServiceAccounts) Get(name string) (*api.ServiceAccount, error) {
obj, err := c.Fake.Invokes(FakeAction{Action: "get-serviceaccount", Value: name}, &api.ServiceAccount{})
return obj.(*api.ServiceAccount), err
}
func (c *FakeServiceAccounts) Create(serviceAccount *api.ServiceAccount) (*api.ServiceAccount, error) {
obj, err := c.Fake.Invokes(FakeAction{Action: "create-serviceaccount", Value: serviceAccount}, &api.ServiceAccount{})
return obj.(*api.ServiceAccount), err
}
func (c *FakeServiceAccounts) Update(serviceAccount *api.ServiceAccount) (*api.ServiceAccount, error) {
obj, err := c.Fake.Invokes(FakeAction{Action: "update-serviceaccount", Value: serviceAccount}, &api.ServiceAccount{})
return obj.(*api.ServiceAccount), err
}
func (c *FakeServiceAccounts) Delete(name string) error {
_, err := c.Fake.Invokes(FakeAction{Action: "delete-serviceaccount", Value: name}, &api.ServiceAccount{})
return err
}
func (c *FakeServiceAccounts) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "watch-serviceAccounts", Value: resourceVersion})
return c.Fake.Watch, c.Fake.Err
}

View File

@ -107,6 +107,10 @@ func (c *Fake) Services(namespace string) client.ServiceInterface {
return &FakeServices{Fake: c, Namespace: namespace}
}
func (c *Fake) ServiceAccounts(namespace string) client.ServiceAccountsInterface {
return &FakeServiceAccounts{Fake: c, Namespace: namespace}
}
func (c *Fake) Secrets(namespace string) client.SecretsInterface {
return &FakeSecrets{Fake: c, Namespace: namespace}
}

View File

@ -66,6 +66,7 @@ func describerMap(c *client.Client) map[string]Describer {
"ReplicationController": &ReplicationControllerDescriber{c},
"Secret": &SecretDescriber{c},
"Service": &ServiceDescriber{c},
"ServiceAccount": &ServiceAccountDescriber{c},
"Minion": &NodeDescriber{c},
"Node": &NodeDescriber{c},
"LimitRange": &LimitRangeDescriber{c},
@ -510,6 +511,67 @@ func describeService(service *api.Service, endpoints *api.Endpoints, events *api
})
}
// ServiceAccountDescriber generates information about a service.
type ServiceAccountDescriber struct {
client.Interface
}
func (d *ServiceAccountDescriber) Describe(namespace, name string) (string, error) {
c := d.ServiceAccounts(namespace)
serviceAccount, err := c.Get(name)
if err != nil {
return "", err
}
tokens := []api.Secret{}
tokenSelector := fields.SelectorFromSet(map[string]string{client.SecretType: string(api.SecretTypeServiceAccountToken)})
secrets, err := d.Secrets(namespace).List(labels.Everything(), tokenSelector)
if err == nil {
for _, s := range secrets.Items {
name, _ := s.Annotations[api.ServiceAccountNameKey]
uid, _ := s.Annotations[api.ServiceAccountUIDKey]
if name == serviceAccount.Name && uid == string(serviceAccount.UID) {
tokens = append(tokens, s)
}
}
}
return describeServiceAccount(serviceAccount, tokens)
}
func describeServiceAccount(serviceAccount *api.ServiceAccount, tokens []api.Secret) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", serviceAccount.Name)
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(serviceAccount.Labels))
if len(serviceAccount.Secrets) == 0 {
fmt.Fprintf(out, "Secrets:\t<none>\n")
} else {
prefix := "Secrets:"
for _, s := range serviceAccount.Secrets {
fmt.Fprintf(out, "%s\t%s\n", prefix, s)
prefix = " "
}
fmt.Fprintln(out)
}
if len(tokens) == 0 {
fmt.Fprintf(out, "Tokens: \t<none>\n")
} else {
prefix := "Tokens: "
for _, t := range tokens {
fmt.Fprintf(out, "%s\t%s\n", prefix, t.Name)
prefix = " "
}
fmt.Fprintln(out)
}
return nil
})
}
// NodeDescriber generates information about a node.
type NodeDescriber struct {
client.Interface

View File

@ -255,6 +255,7 @@ var limitRangeColumns = []string{"NAME"}
var resourceQuotaColumns = []string{"NAME"}
var namespaceColumns = []string{"NAME", "LABELS", "STATUS"}
var secretColumns = []string{"NAME", "TYPE", "DATA"}
var serviceAccountColumns = []string{"NAME", "SECRETS"}
var persistentVolumeColumns = []string{"NAME", "LABELS", "CAPACITY", "ACCESSMODES", "STATUS", "CLAIM"}
var persistentVolumeClaimColumns = []string{"NAME", "LABELS", "STATUS", "VOLUME"}
var componentStatusColumns = []string{"NAME", "STATUS", "MESSAGE", "ERROR"}
@ -283,6 +284,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
h.Handler(namespaceColumns, printNamespaceList)
h.Handler(secretColumns, printSecret)
h.Handler(secretColumns, printSecretList)
h.Handler(serviceAccountColumns, printServiceAccount)
h.Handler(serviceAccountColumns, printServiceAccountList)
h.Handler(persistentVolumeClaimColumns, printPersistentVolumeClaim)
h.Handler(persistentVolumeClaimColumns, printPersistentVolumeClaimList)
h.Handler(persistentVolumeColumns, printPersistentVolume)
@ -596,6 +599,21 @@ func printSecretList(list *api.SecretList, w io.Writer) error {
return nil
}
func printServiceAccount(item *api.ServiceAccount, w io.Writer) error {
_, err := fmt.Fprintf(w, "%s\t%d\n", item.Name, len(item.Secrets))
return err
}
func printServiceAccountList(list *api.ServiceAccountList, w io.Writer) error {
for _, item := range list.Items {
if err := printServiceAccount(&item, w); err != nil {
return err
}
}
return nil
}
func printNode(node *api.Node, w io.Writer) error {
conditionMap := make(map[api.NodeConditionType]*api.NodeCondition)
NodeAllConditions := []api.NodeConditionType{api.NodeReady}

View File

@ -65,6 +65,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/service"
ipallocator "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/service/ipallocator"
etcdipallocator "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/service/ipallocator/etcd"
serviceaccountetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/serviceaccount/etcd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/ui"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@ -398,6 +399,7 @@ func (m *Master) init(c *Config) {
resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewStorage(c.EtcdHelper)
secretStorage := secretetcd.NewStorage(c.EtcdHelper)
serviceAccountStorage := serviceaccountetcd.NewStorage(c.EtcdHelper)
persistentVolumeStorage, persistentVolumeStatusStorage := pvetcd.NewStorage(c.EtcdHelper)
persistentVolumeClaimStorage, persistentVolumeClaimStatusStorage := pvcetcd.NewStorage(c.EtcdHelper)
@ -449,6 +451,7 @@ func (m *Master) init(c *Config) {
"namespaces/status": namespaceStatusStorage,
"namespaces/finalize": namespaceFinalizeStorage,
"secrets": secretStorage,
"serviceAccounts": serviceAccountStorage,
"persistentVolumes": persistentVolumeStorage,
"persistentVolumes/status": persistentVolumeStatusStorage,
"persistentVolumeClaims": persistentVolumeClaimStorage,

View File

@ -0,0 +1,19 @@
/*
Copyright 2014 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 serviceaccount provides a Registry interface and a strategy
// implementation for storing ServiceAccount API objects.
package serviceaccount

View File

@ -0,0 +1,64 @@
/*
Copyright 2014 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 etcd
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
etcdgeneric "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic/etcd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/serviceaccount"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
)
// REST implements a RESTStorage for service accounts against etcd
type REST struct {
*etcdgeneric.Etcd
}
const Prefix = "/serviceaccounts"
// NewStorage returns a RESTStorage object that will work against service accounts objects.
func NewStorage(h tools.EtcdHelper) *REST {
store := &etcdgeneric.Etcd{
NewFunc: func() runtime.Object { return &api.ServiceAccount{} },
NewListFunc: func() runtime.Object { return &api.ServiceAccountList{} },
KeyRootFunc: func(ctx api.Context) string {
return etcdgeneric.NamespaceKeyRootFunc(ctx, Prefix)
},
KeyFunc: func(ctx api.Context, name string) (string, error) {
return etcdgeneric.NamespaceKeyFunc(ctx, Prefix, name)
},
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*api.ServiceAccount).Name, nil
},
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
return serviceaccount.Matcher(label, field)
},
EndpointName: "serviceaccounts",
Helper: h,
}
store.CreateStrategy = serviceaccount.Strategy
store.UpdateStrategy = serviceaccount.Strategy
store.ReturnDeletedObject = true
return &REST{store}
}

View File

@ -0,0 +1,86 @@
/*
Copyright 2014 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 etcd
import (
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest/resttest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools/etcdtest"
)
func newHelper(t *testing.T) (*tools.FakeEtcdClient, tools.EtcdHelper) {
fakeEtcdClient := tools.NewFakeEtcdClient(t)
fakeEtcdClient.TestIndex = true
helper := tools.NewEtcdHelper(fakeEtcdClient, testapi.Codec(), etcdtest.PathPrefix())
return fakeEtcdClient, helper
}
func validNewServiceAccount(name string) *api.ServiceAccount {
return &api.ServiceAccount{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: api.NamespaceDefault,
},
Secrets: []api.ObjectReference{},
}
}
func TestCreate(t *testing.T) {
fakeEtcdClient, helper := newHelper(t)
storage := NewStorage(helper)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
serviceAccount := validNewServiceAccount("foo")
serviceAccount.Name = ""
serviceAccount.GenerateName = "foo-"
test.TestCreate(
// valid
serviceAccount,
// invalid
&api.ServiceAccount{},
&api.ServiceAccount{
ObjectMeta: api.ObjectMeta{Name: "name with spaces"},
},
)
}
func TestUpdate(t *testing.T) {
fakeEtcdClient, helper := newHelper(t)
storage := NewStorage(helper)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
key := etcdtest.AddPrefix("serviceaccounts/default/foo")
fakeEtcdClient.ExpectNotFoundGet(key)
fakeEtcdClient.ChangeIndex = 2
serviceAccount := validNewServiceAccount("foo")
existing := validNewServiceAccount("exists")
obj, err := storage.Create(api.NewDefaultContext(), existing)
if err != nil {
t.Fatalf("unable to create object: %v", err)
}
older := obj.(*api.ServiceAccount)
older.ResourceVersion = "1"
test.TestUpdate(
serviceAccount,
existing,
older,
)
}

View File

@ -0,0 +1,87 @@
/*
Copyright 2014 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 serviceaccount
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
// Registry is an interface implemented by things that know how to store ServiceAccount objects.
type Registry interface {
// ListServiceAccounts obtains a list of ServiceAccounts having labels which match selector.
ListServiceAccounts(ctx api.Context, selector labels.Selector) (*api.ServiceAccountList, error)
// Watch for new/changed/deleted service accounts
WatchServiceAccounts(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
// Get a specific ServiceAccount
GetServiceAccount(ctx api.Context, name string) (*api.ServiceAccount, error)
// Create a ServiceAccount based on a specification.
CreateServiceAccount(ctx api.Context, ServiceAccount *api.ServiceAccount) error
// Update an existing ServiceAccount
UpdateServiceAccount(ctx api.Context, ServiceAccount *api.ServiceAccount) error
// Delete an existing ServiceAccount
DeleteServiceAccount(ctx api.Context, name string) error
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListServiceAccounts(ctx api.Context, label labels.Selector) (*api.ServiceAccountList, error) {
obj, err := s.List(ctx, label, fields.Everything())
if err != nil {
return nil, err
}
return obj.(*api.ServiceAccountList), nil
}
func (s *storage) WatchServiceAccounts(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
return s.Watch(ctx, label, field, resourceVersion)
}
func (s *storage) GetServiceAccount(ctx api.Context, name string) (*api.ServiceAccount, error) {
obj, err := s.Get(ctx, name)
if err != nil {
return nil, err
}
return obj.(*api.ServiceAccount), nil
}
func (s *storage) CreateServiceAccount(ctx api.Context, serviceAccount *api.ServiceAccount) error {
_, err := s.Create(ctx, serviceAccount)
return err
}
func (s *storage) UpdateServiceAccount(ctx api.Context, serviceAccount *api.ServiceAccount) error {
_, _, err := s.Update(ctx, serviceAccount)
return err
}
func (s *storage) DeleteServiceAccount(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View File

@ -0,0 +1,88 @@
/*
Copyright 2014 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 serviceaccount
import (
"fmt"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
)
// strategy implements behavior for ServiceAccount objects
type strategy struct {
runtime.ObjectTyper
api.NameGenerator
}
// Strategy is the default logic that applies when creating and updating ServiceAccount
// objects via the REST API.
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
func (strategy) NamespaceScoped() bool {
return true
}
func (strategy) PrepareForCreate(obj runtime.Object) {
cleanSecretReferences(obj.(*api.ServiceAccount))
}
func (strategy) Validate(ctx api.Context, obj runtime.Object) fielderrors.ValidationErrorList {
return validation.ValidateServiceAccount(obj.(*api.ServiceAccount))
}
func (strategy) AllowCreateOnUpdate() bool {
return false
}
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
cleanSecretReferences(obj.(*api.ServiceAccount))
}
func cleanSecretReferences(serviceAccount *api.ServiceAccount) {
for i, secret := range serviceAccount.Secrets {
serviceAccount.Secrets[i] = api.ObjectReference{Name: secret.Name}
}
}
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
return validation.ValidateServiceAccountUpdate(old.(*api.ServiceAccount), obj.(*api.ServiceAccount))
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
sa, ok := obj.(*api.ServiceAccount)
if !ok {
return false, fmt.Errorf("not a serviceaccount")
}
fields := SelectableFields(sa)
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
})
}
// SelectableFields returns a label set that represents the object
func SelectableFields(obj *api.ServiceAccount) labels.Set {
return labels.Set{
"metadata.name": obj.Name,
}
}