mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-11 04:52:08 +00:00
Secret API resource
This commit is contained in:
parent
48a98f97a1
commit
fb001ada21
@ -52,6 +52,8 @@ func init() {
|
|||||||
&ResourceQuotaUsage{},
|
&ResourceQuotaUsage{},
|
||||||
&Namespace{},
|
&Namespace{},
|
||||||
&NamespaceList{},
|
&NamespaceList{},
|
||||||
|
&Secret{},
|
||||||
|
&SecretList{},
|
||||||
)
|
)
|
||||||
// Legacy names are supported
|
// Legacy names are supported
|
||||||
Scheme.AddKnownTypeWithName("", "Minion", &Node{})
|
Scheme.AddKnownTypeWithName("", "Minion", &Node{})
|
||||||
@ -85,3 +87,5 @@ func (*ResourceQuotaList) IsAnAPIObject() {}
|
|||||||
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
||||||
func (*Namespace) IsAnAPIObject() {}
|
func (*Namespace) IsAnAPIObject() {}
|
||||||
func (*NamespaceList) IsAnAPIObject() {}
|
func (*NamespaceList) IsAnAPIObject() {}
|
||||||
|
func (*Secret) IsAnAPIObject() {}
|
||||||
|
func (*SecretList) IsAnAPIObject() {}
|
||||||
|
@ -227,6 +227,12 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
|
|||||||
c.Fuzz(&e.Count)
|
c.Fuzz(&e.Count)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
func(s *api.Secret, c fuzz.Continue) {
|
||||||
|
c.Fuzz(&s.TypeMeta)
|
||||||
|
c.Fuzz(&s.ObjectMeta)
|
||||||
|
|
||||||
|
s.Type = api.SecretTypeOpaque
|
||||||
|
},
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
@ -178,6 +178,8 @@ type VolumeSource struct {
|
|||||||
GCEPersistentDisk *GCEPersistentDisk `json:"persistentDisk"`
|
GCEPersistentDisk *GCEPersistentDisk `json:"persistentDisk"`
|
||||||
// GitRepo represents a git repository at a particular revision.
|
// GitRepo represents a git repository at a particular revision.
|
||||||
GitRepo *GitRepo `json:"gitRepo"`
|
GitRepo *GitRepo `json:"gitRepo"`
|
||||||
|
// Secret represents a secret that should populate this volume.
|
||||||
|
Secret *SecretSource `json:"secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HostPath represents bare host directory volume.
|
// HostPath represents bare host directory volume.
|
||||||
@ -228,6 +230,12 @@ type GitRepo struct {
|
|||||||
// TODO: Consider credentials here.
|
// TODO: Consider credentials here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adapts a Secret into a VolumeSource
|
||||||
|
type SecretSource struct {
|
||||||
|
// Reference to a Secret
|
||||||
|
Target ObjectReference `json:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
// Port represents a network port in a single container
|
// Port represents a network port in a single container
|
||||||
type Port struct {
|
type Port struct {
|
||||||
// Optional: If specified, this must be a DNS_LABEL. Each named port
|
// Optional: If specified, this must be a DNS_LABEL. Each named port
|
||||||
@ -1309,3 +1317,27 @@ type ResourceQuotaList struct {
|
|||||||
// Items is a list of ResourceQuota objects
|
// Items is a list of ResourceQuota objects
|
||||||
Items []ResourceQuota `json:"items"`
|
Items []ResourceQuota `json:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Secret holds secret data of a certain type
|
||||||
|
type Secret struct {
|
||||||
|
TypeMeta `json:",inline"`
|
||||||
|
ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Data map[string][]byte `json:"data,omitempty"`
|
||||||
|
Type SecretType `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecretType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SecretTypeOpaque SecretType = "opaque" // Default; arbitrary user-defined data
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecretList struct {
|
||||||
|
TypeMeta `json:",inline"`
|
||||||
|
ListMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Items []Secret `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaxSecretSize = 1 * 1024 * 1024
|
||||||
|
@ -1028,6 +1028,9 @@ func init() {
|
|||||||
if err := s.Convert(&in.HostPath, &out.HostDir, 0); err != nil {
|
if err := s.Convert(&in.HostPath, &out.HostDir, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.Convert(&in.Secret, &out.Secret, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
func(in *VolumeSource, out *newer.VolumeSource, s conversion.Scope) error {
|
func(in *VolumeSource, out *newer.VolumeSource, s conversion.Scope) error {
|
||||||
@ -1043,6 +1046,9 @@ func init() {
|
|||||||
if err := s.Convert(&in.HostDir, &out.HostPath, 0); err != nil {
|
if err := s.Convert(&in.HostDir, &out.HostPath, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.Convert(&in.Secret, &out.Secret, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -80,5 +80,10 @@ func init() {
|
|||||||
obj.TimeoutSeconds = 1
|
obj.TimeoutSeconds = 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
func(obj *Secret) {
|
||||||
|
if obj.Type == "" {
|
||||||
|
obj.Type = SecretTypeOpaque
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -92,3 +92,13 @@ func TestSetDefaultContainer(t *testing.T) {
|
|||||||
current.ProtocolTCP, container.Ports[0].Protocol)
|
current.ProtocolTCP, container.Ports[0].Protocol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetDefaultSecret(t *testing.T) {
|
||||||
|
s := ¤t.Secret{}
|
||||||
|
obj2 := roundTrip(t, runtime.Object(s))
|
||||||
|
s2 := obj2.(*current.Secret)
|
||||||
|
|
||||||
|
if s2.Type != current.SecretTypeOpaque {
|
||||||
|
t.Errorf("Expected secret type %v, got %v", current.SecretTypeOpaque, s2.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -53,6 +53,8 @@ func init() {
|
|||||||
&ResourceQuotaUsage{},
|
&ResourceQuotaUsage{},
|
||||||
&Namespace{},
|
&Namespace{},
|
||||||
&NamespaceList{},
|
&NamespaceList{},
|
||||||
|
&Secret{},
|
||||||
|
&SecretList{},
|
||||||
)
|
)
|
||||||
// Future names are supported
|
// Future names are supported
|
||||||
api.Scheme.AddKnownTypeWithName("v1beta1", "Node", &Minion{})
|
api.Scheme.AddKnownTypeWithName("v1beta1", "Node", &Minion{})
|
||||||
@ -86,3 +88,5 @@ func (*ResourceQuotaList) IsAnAPIObject() {}
|
|||||||
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
||||||
func (*Namespace) IsAnAPIObject() {}
|
func (*Namespace) IsAnAPIObject() {}
|
||||||
func (*NamespaceList) IsAnAPIObject() {}
|
func (*NamespaceList) IsAnAPIObject() {}
|
||||||
|
func (*Secret) IsAnAPIObject() {}
|
||||||
|
func (*SecretList) IsAnAPIObject() {}
|
||||||
|
@ -103,6 +103,8 @@ type VolumeSource struct {
|
|||||||
GCEPersistentDisk *GCEPersistentDisk `json:"persistentDisk" description:"GCE disk resource attached to the host machine on demand"`
|
GCEPersistentDisk *GCEPersistentDisk `json:"persistentDisk" description:"GCE disk resource attached to the host machine on demand"`
|
||||||
// GitRepo represents a git repository at a particular revision.
|
// GitRepo represents a git repository at a particular revision.
|
||||||
GitRepo *GitRepo `json:"gitRepo" description:"git repository at a particular revision"`
|
GitRepo *GitRepo `json:"gitRepo" description:"git repository at a particular revision"`
|
||||||
|
// Secret represents a secret to populate the volume with
|
||||||
|
Secret *SecretSource `json:"secret" description:"secret to populate volume with"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HostPath represents bare host directory volume.
|
// HostPath represents bare host directory volume.
|
||||||
@ -153,6 +155,12 @@ type GitRepo struct {
|
|||||||
Revision string `json:"revision" description:"commit hash for the specified revision"`
|
Revision string `json:"revision" description:"commit hash for the specified revision"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adapts a Secret into a VolumeSource
|
||||||
|
type SecretSource struct {
|
||||||
|
// Reference to a Secret
|
||||||
|
Target ObjectReference `json:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
// Port represents a network port in a single container
|
// Port represents a network port in a single container
|
||||||
type Port struct {
|
type Port struct {
|
||||||
// Optional: If specified, this must be a DNS_LABEL. Each named port
|
// Optional: If specified, this must be a DNS_LABEL. Each named port
|
||||||
@ -1091,3 +1099,22 @@ type ResourceQuotaList struct {
|
|||||||
// Items is a list of ResourceQuota objects
|
// Items is a list of ResourceQuota objects
|
||||||
Items []ResourceQuota `json:"items"`
|
Items []ResourceQuota `json:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Secret struct {
|
||||||
|
TypeMeta `json:",inline"`
|
||||||
|
|
||||||
|
Data map[string][]byte `json:"data,omitempty"`
|
||||||
|
Type SecretType `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecretType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SecretTypeOpaque SecretType = "opaque" // Default; arbitrary user-defined data
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecretList struct {
|
||||||
|
TypeMeta `json:",inline"`
|
||||||
|
|
||||||
|
Items []Secret `json:"items"`
|
||||||
|
}
|
||||||
|
@ -943,6 +943,9 @@ func init() {
|
|||||||
if err := s.Convert(&in.HostPath, &out.HostDir, 0); err != nil {
|
if err := s.Convert(&in.HostPath, &out.HostDir, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.Convert(&in.Secret, &out.Secret, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
func(in *VolumeSource, out *newer.VolumeSource, s conversion.Scope) error {
|
func(in *VolumeSource, out *newer.VolumeSource, s conversion.Scope) error {
|
||||||
@ -958,6 +961,9 @@ func init() {
|
|||||||
if err := s.Convert(&in.HostDir, &out.HostPath, 0); err != nil {
|
if err := s.Convert(&in.HostDir, &out.HostPath, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.Convert(&in.Secret, &out.Secret, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -21,12 +21,14 @@ import (
|
|||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
api.Scheme.AddDefaultingFuncs(
|
api.Scheme.AddDefaultingFuncs(
|
||||||
func(obj *Volume) {
|
func(obj *Volume) {
|
||||||
if util.AllPtrFieldsNil(&obj.Source) {
|
if util.AllPtrFieldsNil(&obj.Source) {
|
||||||
|
glog.Errorf("Defaulting volume source for %v", obj)
|
||||||
obj.Source = VolumeSource{
|
obj.Source = VolumeSource{
|
||||||
EmptyDir: &EmptyDir{},
|
EmptyDir: &EmptyDir{},
|
||||||
}
|
}
|
||||||
@ -80,5 +82,10 @@ func init() {
|
|||||||
obj.TimeoutSeconds = 1
|
obj.TimeoutSeconds = 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
func(obj *Secret) {
|
||||||
|
if obj.Type == "" {
|
||||||
|
obj.Type = SecretTypeOpaque
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -92,3 +92,13 @@ func TestSetDefaultContainer(t *testing.T) {
|
|||||||
current.ProtocolTCP, container.Ports[0].Protocol)
|
current.ProtocolTCP, container.Ports[0].Protocol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetDefaultSecret(t *testing.T) {
|
||||||
|
s := ¤t.Secret{}
|
||||||
|
obj2 := roundTrip(t, runtime.Object(s))
|
||||||
|
s2 := obj2.(*current.Secret)
|
||||||
|
|
||||||
|
if s2.Type != current.SecretTypeOpaque {
|
||||||
|
t.Errorf("Expected secret type %v, got %v", current.SecretTypeOpaque, s2.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -53,6 +53,8 @@ func init() {
|
|||||||
&ResourceQuotaUsage{},
|
&ResourceQuotaUsage{},
|
||||||
&Namespace{},
|
&Namespace{},
|
||||||
&NamespaceList{},
|
&NamespaceList{},
|
||||||
|
&Secret{},
|
||||||
|
&SecretList{},
|
||||||
)
|
)
|
||||||
// Future names are supported
|
// Future names are supported
|
||||||
api.Scheme.AddKnownTypeWithName("v1beta2", "Node", &Minion{})
|
api.Scheme.AddKnownTypeWithName("v1beta2", "Node", &Minion{})
|
||||||
@ -86,3 +88,5 @@ func (*ResourceQuotaList) IsAnAPIObject() {}
|
|||||||
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
||||||
func (*Namespace) IsAnAPIObject() {}
|
func (*Namespace) IsAnAPIObject() {}
|
||||||
func (*NamespaceList) IsAnAPIObject() {}
|
func (*NamespaceList) IsAnAPIObject() {}
|
||||||
|
func (*Secret) IsAnAPIObject() {}
|
||||||
|
func (*SecretList) IsAnAPIObject() {}
|
||||||
|
@ -72,6 +72,8 @@ type VolumeSource struct {
|
|||||||
GCEPersistentDisk *GCEPersistentDisk `json:"persistentDisk" description:"GCE disk resource attached to the host machine on demand"`
|
GCEPersistentDisk *GCEPersistentDisk `json:"persistentDisk" description:"GCE disk resource attached to the host machine on demand"`
|
||||||
// GitRepo represents a git repository at a particular revision.
|
// GitRepo represents a git repository at a particular revision.
|
||||||
GitRepo *GitRepo `json:"gitRepo" description:"git repository at a particular revision"`
|
GitRepo *GitRepo `json:"gitRepo" description:"git repository at a particular revision"`
|
||||||
|
// Secret is a secret to populate the volume with
|
||||||
|
Secret *SecretSource `json:"secret" description:"secret to populate volume"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HostPath represents bare host directory volume.
|
// HostPath represents bare host directory volume.
|
||||||
@ -81,6 +83,12 @@ type HostPath struct {
|
|||||||
|
|
||||||
type EmptyDir struct{}
|
type EmptyDir struct{}
|
||||||
|
|
||||||
|
// Adapts a Secret into a VolumeSource
|
||||||
|
type SecretSource struct {
|
||||||
|
// Reference to a Secret
|
||||||
|
Target ObjectReference `json:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
// Protocol defines network protocols supported for things like conatiner ports.
|
// Protocol defines network protocols supported for things like conatiner ports.
|
||||||
type Protocol string
|
type Protocol string
|
||||||
|
|
||||||
@ -1094,3 +1102,23 @@ type ResourceQuotaList struct {
|
|||||||
// Items is a list of ResourceQuota objects
|
// Items is a list of ResourceQuota objects
|
||||||
Items []ResourceQuota `json:"items"`
|
Items []ResourceQuota `json:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Secret holds secret data of a certain type
|
||||||
|
type Secret struct {
|
||||||
|
TypeMeta `json:",inline"`
|
||||||
|
|
||||||
|
Data map[string][]byte `json:"data,omitempty"`
|
||||||
|
Type SecretType `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecretType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SecretTypeOpaque SecretType = "opaque" // Default; arbitrary user-defined data
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecretList struct {
|
||||||
|
TypeMeta `json:",inline"`
|
||||||
|
|
||||||
|
Items []Secret `json:"items"`
|
||||||
|
}
|
||||||
|
@ -75,5 +75,10 @@ func init() {
|
|||||||
obj.TimeoutSeconds = 1
|
obj.TimeoutSeconds = 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
func(obj *Secret) {
|
||||||
|
if obj.Type == "" {
|
||||||
|
obj.Type = SecretTypeOpaque
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -92,3 +92,13 @@ func TestSetDefaultContainer(t *testing.T) {
|
|||||||
current.ProtocolTCP, container.Ports[0].Protocol)
|
current.ProtocolTCP, container.Ports[0].Protocol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetDefaultSecret(t *testing.T) {
|
||||||
|
s := ¤t.Secret{}
|
||||||
|
obj2 := roundTrip(t, runtime.Object(s))
|
||||||
|
s2 := obj2.(*current.Secret)
|
||||||
|
|
||||||
|
if s2.Type != current.SecretTypeOpaque {
|
||||||
|
t.Errorf("Expected secret type %v, got %v", current.SecretTypeOpaque, s2.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -53,6 +53,8 @@ func init() {
|
|||||||
&ResourceQuotaUsage{},
|
&ResourceQuotaUsage{},
|
||||||
&Namespace{},
|
&Namespace{},
|
||||||
&NamespaceList{},
|
&NamespaceList{},
|
||||||
|
&Secret{},
|
||||||
|
&SecretList{},
|
||||||
)
|
)
|
||||||
// Legacy names are supported
|
// Legacy names are supported
|
||||||
api.Scheme.AddKnownTypeWithName("v1beta3", "Minion", &Node{})
|
api.Scheme.AddKnownTypeWithName("v1beta3", "Minion", &Node{})
|
||||||
@ -86,3 +88,5 @@ func (*ResourceQuotaList) IsAnAPIObject() {}
|
|||||||
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
func (*ResourceQuotaUsage) IsAnAPIObject() {}
|
||||||
func (*Namespace) IsAnAPIObject() {}
|
func (*Namespace) IsAnAPIObject() {}
|
||||||
func (*NamespaceList) IsAnAPIObject() {}
|
func (*NamespaceList) IsAnAPIObject() {}
|
||||||
|
func (*Secret) IsAnAPIObject() {}
|
||||||
|
func (*SecretList) IsAnAPIObject() {}
|
||||||
|
@ -197,6 +197,8 @@ type VolumeSource struct {
|
|||||||
GCEPersistentDisk *GCEPersistentDisk `json:"gcePersistentDisk"`
|
GCEPersistentDisk *GCEPersistentDisk `json:"gcePersistentDisk"`
|
||||||
// GitRepo represents a git repository at a particular revision.
|
// GitRepo represents a git repository at a particular revision.
|
||||||
GitRepo *GitRepo `json:"gitRepo"`
|
GitRepo *GitRepo `json:"gitRepo"`
|
||||||
|
// Secret represents a secret that should populate this volume.
|
||||||
|
Secret *SecretSource `json:"secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HostPath represents bare host directory volume.
|
// HostPath represents bare host directory volume.
|
||||||
@ -246,6 +248,12 @@ type GitRepo struct {
|
|||||||
Revision string `json:"revision"`
|
Revision string `json:"revision"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adapts a Secret into a VolumeSource
|
||||||
|
type SecretSource struct {
|
||||||
|
// Reference to a Secret
|
||||||
|
Target ObjectReference `json:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
// Port represents a network port in a single container.
|
// Port represents a network port in a single container.
|
||||||
type Port struct {
|
type Port struct {
|
||||||
// Optional: If specified, this must be a DNS_LABEL. Each named port
|
// Optional: If specified, this must be a DNS_LABEL. Each named port
|
||||||
@ -1234,3 +1242,26 @@ type ResourceQuotaList struct {
|
|||||||
// Items is a list of ResourceQuota objects
|
// Items is a list of ResourceQuota objects
|
||||||
Items []ResourceQuota `json:"items"`
|
Items []ResourceQuota `json:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Secret holds mappings between paths and secret data
|
||||||
|
// TODO: shouldn't "Secret" be a plural?
|
||||||
|
type Secret struct {
|
||||||
|
TypeMeta `json:",inline"`
|
||||||
|
ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Data map[string][]byte `json:"data,omitempty"`
|
||||||
|
Type SecretType `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecretType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SecretTypeOpaque SecretType = "opaque" // Default; arbitrary user-defined data
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecretList struct {
|
||||||
|
TypeMeta `json:",inline"`
|
||||||
|
ListMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Items []Secret `json:"items"`
|
||||||
|
}
|
||||||
|
@ -247,6 +247,10 @@ func validateSource(source *api.VolumeSource) errs.ValidationErrorList {
|
|||||||
numVolumes++
|
numVolumes++
|
||||||
allErrs = append(allErrs, validateGCEPersistentDisk(source.GCEPersistentDisk).Prefix("persistentDisk")...)
|
allErrs = append(allErrs, validateGCEPersistentDisk(source.GCEPersistentDisk).Prefix("persistentDisk")...)
|
||||||
}
|
}
|
||||||
|
if source.Secret != nil {
|
||||||
|
numVolumes++
|
||||||
|
allErrs = append(allErrs, validateSecretSource(source.Secret).Prefix("secret")...)
|
||||||
|
}
|
||||||
if numVolumes != 1 {
|
if numVolumes != 1 {
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("", source, "exactly 1 volume type is required"))
|
allErrs = append(allErrs, errs.NewFieldInvalid("", source, "exactly 1 volume type is required"))
|
||||||
}
|
}
|
||||||
@ -283,6 +287,20 @@ func validateGCEPersistentDisk(PD *api.GCEPersistentDisk) errs.ValidationErrorLi
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateSecretSource(secretSource *api.SecretSource) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
if secretSource.Target.Name == "" {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldRequired("target.name", ""))
|
||||||
|
}
|
||||||
|
if secretSource.Target.Namespace == "" {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldRequired("target.namespace", ""))
|
||||||
|
}
|
||||||
|
if secretSource.Target.Kind != "Secret" {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldInvalid("target.kind", secretSource.Target.Kind, "Secret"))
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
var supportedPortProtocols = util.NewStringSet(string(api.ProtocolTCP), string(api.ProtocolUDP))
|
var supportedPortProtocols = util.NewStringSet(string(api.ProtocolTCP), string(api.ProtocolUDP))
|
||||||
|
|
||||||
func validatePorts(ports []api.Port) errs.ValidationErrorList {
|
func validatePorts(ports []api.Port) errs.ValidationErrorList {
|
||||||
@ -820,6 +838,31 @@ func ValidateLimitRange(limitRange *api.LimitRange) errs.ValidationErrorList {
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateSecret tests if required fields in the Secret are set.
|
||||||
|
func ValidateSecret(secret *api.Secret) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
if len(secret.Name) == 0 {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldRequired("name", secret.Name))
|
||||||
|
} else if !util.IsDNSSubdomain(secret.Name) {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldInvalid("name", secret.Name, ""))
|
||||||
|
}
|
||||||
|
if len(secret.Namespace) == 0 {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldRequired("namespace", secret.Namespace))
|
||||||
|
} else if !util.IsDNSSubdomain(secret.Namespace) {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", secret.Namespace, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSize := 0
|
||||||
|
for _, value := range secret.Data {
|
||||||
|
totalSize += len(value)
|
||||||
|
}
|
||||||
|
if totalSize > api.MaxSecretSize {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldForbidden("data", "Maximum secret size exceeded"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
func validateBasicResource(quantity resource.Quantity) errs.ValidationErrorList {
|
func validateBasicResource(quantity resource.Quantity) errs.ValidationErrorList {
|
||||||
if quantity.Value() < 0 {
|
if quantity.Value() < 0 {
|
||||||
return errs.ValidationErrorList{fmt.Errorf("%v is not a valid resource quantity", quantity.Value())}
|
return errs.ValidationErrorList{fmt.Errorf("%v is not a valid resource quantity", quantity.Value())}
|
||||||
|
@ -153,12 +153,13 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
{Name: "empty", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
|
{Name: "empty", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
|
||||||
{Name: "gcepd", Source: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}},
|
{Name: "gcepd", Source: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}},
|
||||||
{Name: "gitrepo", Source: api.VolumeSource{GitRepo: &api.GitRepo{"my-repo", "hashstring"}}},
|
{Name: "gitrepo", Source: api.VolumeSource{GitRepo: &api.GitRepo{"my-repo", "hashstring"}}},
|
||||||
|
{Name: "secret", Source: api.VolumeSource{Secret: &api.SecretSource{api.ObjectReference{Namespace: api.NamespaceDefault, Name: "my-secret", Kind: "Secret"}}}},
|
||||||
}
|
}
|
||||||
names, errs := validateVolumes(successCase)
|
names, errs := validateVolumes(successCase)
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
if len(names) != 6 || !names.HasAll("abc", "123", "abc-123", "empty", "gcepd", "gitrepo") {
|
if len(names) != len(successCase) || !names.HasAll("abc", "123", "abc-123", "empty", "gcepd", "gitrepo", "secret") {
|
||||||
t.Errorf("wrong names result: %v", names)
|
t.Errorf("wrong names result: %v", names)
|
||||||
}
|
}
|
||||||
emptyVS := api.VolumeSource{EmptyDir: &api.EmptyDir{}}
|
emptyVS := api.VolumeSource{EmptyDir: &api.EmptyDir{}}
|
||||||
@ -2490,3 +2491,52 @@ func TestValidateNamespaceUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateSecret(t *testing.T) {
|
||||||
|
validSecret := func() api.Secret {
|
||||||
|
return api.Secret{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"foo": []byte("bar"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyName = validSecret()
|
||||||
|
invalidName = validSecret()
|
||||||
|
emptyNs = validSecret()
|
||||||
|
invalidNs = validSecret()
|
||||||
|
overMaxSize = validSecret()
|
||||||
|
)
|
||||||
|
|
||||||
|
emptyName.Name = ""
|
||||||
|
invalidName.Name = "NoUppercaseOrSpecialCharsLike=Equals"
|
||||||
|
emptyNs.Namespace = ""
|
||||||
|
invalidNs.Namespace = "NoUppercaseOrSpecialCharsLike=Equals"
|
||||||
|
overMaxSize.Data = map[string][]byte{
|
||||||
|
"over": make([]byte, api.MaxSecretSize+1),
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := map[string]struct {
|
||||||
|
secret api.Secret
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
"valid": {validSecret(), true},
|
||||||
|
"empty name": {emptyName, false},
|
||||||
|
"invalid name": {invalidName, false},
|
||||||
|
"empty namespace": {emptyNs, false},
|
||||||
|
"invalid namespace": {invalidNs, false},
|
||||||
|
"over max size": {overMaxSize, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
errs := ValidateSecret(&tc.secret)
|
||||||
|
if tc.valid && len(errs) > 0 {
|
||||||
|
t.Errorf("%v: Unexpected error: %v", name, errs)
|
||||||
|
}
|
||||||
|
if !tc.valid && len(errs) == 0 {
|
||||||
|
t.Errorf("%v: Unexpected non-error", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -40,6 +40,7 @@ type Interface interface {
|
|||||||
LimitRangesNamespacer
|
LimitRangesNamespacer
|
||||||
ResourceQuotasNamespacer
|
ResourceQuotasNamespacer
|
||||||
ResourceQuotaUsagesNamespacer
|
ResourceQuotaUsagesNamespacer
|
||||||
|
SecretsNamespacer
|
||||||
NamespacesInterface
|
NamespacesInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +80,10 @@ func (c *Client) ResourceQuotaUsages(namespace string) ResourceQuotaUsageInterfa
|
|||||||
return newResourceQuotaUsages(c, namespace)
|
return newResourceQuotaUsages(c, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) Secrets(namespace string) SecretsInterface {
|
||||||
|
return newSecrets(c, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) Namespaces() NamespaceInterface {
|
func (c *Client) Namespaces() NamespaceInterface {
|
||||||
return newNamespaces(c)
|
return newNamespaces(c)
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,8 @@ type Fake struct {
|
|||||||
LimitRangesList api.LimitRangeList
|
LimitRangesList api.LimitRangeList
|
||||||
ResourceQuotasList api.ResourceQuotaList
|
ResourceQuotasList api.ResourceQuotaList
|
||||||
NamespacesList api.NamespaceList
|
NamespacesList api.NamespaceList
|
||||||
|
SecretList api.SecretList
|
||||||
|
Secret api.Secret
|
||||||
Err error
|
Err error
|
||||||
Watch watch.Interface
|
Watch watch.Interface
|
||||||
}
|
}
|
||||||
@ -85,6 +87,10 @@ func (c *Fake) Services(namespace string) ServiceInterface {
|
|||||||
return &FakeServices{Fake: c, Namespace: namespace}
|
return &FakeServices{Fake: c, Namespace: namespace}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Fake) Secrets(namespace string) SecretsInterface {
|
||||||
|
return &FakeSecrets{Fake: c, Namespace: namespace}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Fake) Namespaces() NamespaceInterface {
|
func (c *Fake) Namespaces() NamespaceInterface {
|
||||||
return &FakeNamespaces{Fake: c}
|
return &FakeNamespaces{Fake: c}
|
||||||
}
|
}
|
||||||
|
60
pkg/client/fake_secrets.go
Normal file
60
pkg/client/fake_secrets.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. 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/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fake implements SecretInterface. 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 FakeSecrets struct {
|
||||||
|
Fake *Fake
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeSecrets) List(labels, fields labels.Selector) (*api.SecretList, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "list-secrets"})
|
||||||
|
return &c.Fake.SecretList, c.Fake.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeSecrets) Get(name string) (*api.Secret, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "get-secret", Value: name})
|
||||||
|
return api.Scheme.CopyOrDie(&c.Fake.Secret).(*api.Secret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeSecrets) Create(secret *api.Secret) (*api.Secret, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "create-secret", Value: secret})
|
||||||
|
return &api.Secret{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeSecrets) Update(secret *api.Secret) (*api.Secret, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "update-secret", Value: secret})
|
||||||
|
return &api.Secret{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeSecrets) Delete(secret string) error {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "delete-secret", Value: secret})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeSecrets) Watch(label, field labels.Selector, resourceVersion string) (watch.Interface, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "watch-secrets", Value: resourceVersion})
|
||||||
|
return c.Fake.Watch, c.Fake.Err
|
||||||
|
}
|
140
pkg/client/secrets.go
Normal file
140
pkg/client/secrets.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. 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 (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecretsNamespacer interface {
|
||||||
|
Secrets(namespace string) SecretsInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecretsInterface interface {
|
||||||
|
Create(secret *api.Secret) (*api.Secret, error)
|
||||||
|
Update(secret *api.Secret) (*api.Secret, error)
|
||||||
|
Delete(name string) error
|
||||||
|
List(label, field labels.Selector) (*api.SecretList, error)
|
||||||
|
Get(name string) (*api.Secret, error)
|
||||||
|
Watch(label, field labels.Selector, resourceVersion string) (watch.Interface, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// events implements Secrets interface
|
||||||
|
type secrets struct {
|
||||||
|
client *Client
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSecrets returns a new secrets object.
|
||||||
|
func newSecrets(c *Client, ns string) *secrets {
|
||||||
|
return &secrets{
|
||||||
|
client: c,
|
||||||
|
namespace: ns,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *secrets) Create(secret *api.Secret) (*api.Secret, error) {
|
||||||
|
if s.namespace != "" && secret.Namespace != s.namespace {
|
||||||
|
return nil, fmt.Errorf("can't create a secret with namespace '%v' in namespace '%v'", secret.Namespace, s.namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &api.Secret{}
|
||||||
|
err := s.client.Post().
|
||||||
|
Namespace(secret.Namespace).
|
||||||
|
Resource("secrets").
|
||||||
|
Body(secret).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a list of secrets matching the selectors.
|
||||||
|
func (s *secrets) List(label, field labels.Selector) (*api.SecretList, error) {
|
||||||
|
result := &api.SecretList{}
|
||||||
|
|
||||||
|
err := s.client.Get().
|
||||||
|
Namespace(s.namespace).
|
||||||
|
Resource("secrets").
|
||||||
|
SelectorParam("labels", label).
|
||||||
|
SelectorParam("fields", field).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the given secret, or an error.
|
||||||
|
func (s *secrets) Get(name string) (*api.Secret, error) {
|
||||||
|
if len(name) == 0 {
|
||||||
|
return nil, errors.New("name is required parameter to Get")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &api.Secret{}
|
||||||
|
err := s.client.Get().
|
||||||
|
Namespace(s.namespace).
|
||||||
|
Resource("secrets").
|
||||||
|
Name(name).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch starts watching for secrets matching the given selectors.
|
||||||
|
func (s *secrets) Watch(label, field labels.Selector, resourceVersion string) (watch.Interface, error) {
|
||||||
|
return s.client.Get().
|
||||||
|
Prefix("watch").
|
||||||
|
Namespace(s.namespace).
|
||||||
|
Resource("secrets").
|
||||||
|
Param("resourceVersion", resourceVersion).
|
||||||
|
SelectorParam("labels", label).
|
||||||
|
SelectorParam("fields", field).
|
||||||
|
Watch()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *secrets) Delete(name string) error {
|
||||||
|
return s.client.Delete().
|
||||||
|
Namespace(s.namespace).
|
||||||
|
Resource("secrets").
|
||||||
|
Name(name).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *secrets) Update(secret *api.Secret) (result *api.Secret, err error) {
|
||||||
|
result = &api.Secret{}
|
||||||
|
if len(secret.ResourceVersion) == 0 {
|
||||||
|
err = fmt.Errorf("invalid update object, missing resource version: %v", secret)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.client.Put().
|
||||||
|
Namespace(s.namespace).
|
||||||
|
Resource("secrets").
|
||||||
|
Name(secret.Name).
|
||||||
|
Body(secret).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -225,6 +225,7 @@ var eventColumns = []string{"FIRSTSEEN", "LASTSEEN", "COUNT", "NAME", "KIND", "S
|
|||||||
var limitRangeColumns = []string{"NAME"}
|
var limitRangeColumns = []string{"NAME"}
|
||||||
var resourceQuotaColumns = []string{"NAME"}
|
var resourceQuotaColumns = []string{"NAME"}
|
||||||
var namespaceColumns = []string{"NAME", "LABELS"}
|
var namespaceColumns = []string{"NAME", "LABELS"}
|
||||||
|
var secretColumns = []string{"NAME", "DATA"}
|
||||||
|
|
||||||
// addDefaultHandlers adds print handlers for default Kubernetes types.
|
// addDefaultHandlers adds print handlers for default Kubernetes types.
|
||||||
func (h *HumanReadablePrinter) addDefaultHandlers() {
|
func (h *HumanReadablePrinter) addDefaultHandlers() {
|
||||||
@ -246,6 +247,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
|
|||||||
h.Handler(resourceQuotaColumns, printResourceQuotaList)
|
h.Handler(resourceQuotaColumns, printResourceQuotaList)
|
||||||
h.Handler(namespaceColumns, printNamespace)
|
h.Handler(namespaceColumns, printNamespace)
|
||||||
h.Handler(namespaceColumns, printNamespaceList)
|
h.Handler(namespaceColumns, printNamespaceList)
|
||||||
|
h.Handler(secretColumns, printSecret)
|
||||||
|
h.Handler(secretColumns, printSecretList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
|
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
|
||||||
@ -383,6 +386,21 @@ func printNamespaceList(list *api.NamespaceList, w io.Writer) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printSecret(item *api.Secret, w io.Writer) error {
|
||||||
|
_, err := fmt.Fprintf(w, "%s\t%v\n", item.Name, len(item.Data))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func printSecretList(list *api.SecretList, w io.Writer) error {
|
||||||
|
for _, item := range list.Items {
|
||||||
|
if err := printSecret(&item, w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func printMinion(minion *api.Node, w io.Writer) error {
|
func printMinion(minion *api.Node, w io.Writer) error {
|
||||||
conditionMap := make(map[api.NodeConditionKind]*api.NodeCondition)
|
conditionMap := make(map[api.NodeConditionKind]*api.NodeCondition)
|
||||||
NodeAllConditions := []api.NodeConditionKind{api.NodeReady, api.NodeReachable}
|
NodeAllConditions := []api.NodeConditionKind{api.NodeReady, api.NodeReachable}
|
||||||
|
@ -54,6 +54,7 @@ import (
|
|||||||
podetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod/etcd"
|
podetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod/etcd"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/resourcequota"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/resourcequota"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/resourcequotausage"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/resourcequotausage"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/secret"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/service"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/service"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
@ -372,6 +373,7 @@ func (m *Master) init(c *Config) {
|
|||||||
eventRegistry := event.NewEtcdRegistry(c.EtcdHelper, uint64(c.EventTTL.Seconds()))
|
eventRegistry := event.NewEtcdRegistry(c.EtcdHelper, uint64(c.EventTTL.Seconds()))
|
||||||
limitRangeRegistry := limitrange.NewEtcdRegistry(c.EtcdHelper)
|
limitRangeRegistry := limitrange.NewEtcdRegistry(c.EtcdHelper)
|
||||||
resourceQuotaRegistry := resourcequota.NewEtcdRegistry(c.EtcdHelper)
|
resourceQuotaRegistry := resourcequota.NewEtcdRegistry(c.EtcdHelper)
|
||||||
|
secretRegistry := secret.NewEtcdRegistry(c.EtcdHelper)
|
||||||
m.namespaceRegistry = namespace.NewEtcdRegistry(c.EtcdHelper)
|
m.namespaceRegistry = namespace.NewEtcdRegistry(c.EtcdHelper)
|
||||||
|
|
||||||
// TODO: split me up into distinct storage registries
|
// TODO: split me up into distinct storage registries
|
||||||
@ -411,6 +413,7 @@ func (m *Master) init(c *Config) {
|
|||||||
"resourceQuotas": resourcequota.NewREST(resourceQuotaRegistry),
|
"resourceQuotas": resourcequota.NewREST(resourceQuotaRegistry),
|
||||||
"resourceQuotaUsages": resourcequotausage.NewREST(resourceQuotaRegistry),
|
"resourceQuotaUsages": resourcequotausage.NewREST(resourceQuotaRegistry),
|
||||||
"namespaces": namespace.NewREST(m.namespaceRegistry),
|
"namespaces": namespace.NewREST(m.namespaceRegistry),
|
||||||
|
"secrets": secret.NewREST(secretRegistry),
|
||||||
}
|
}
|
||||||
|
|
||||||
apiVersions := []string{"v1beta1", "v1beta2"}
|
apiVersions := []string{"v1beta1", "v1beta2"}
|
||||||
|
19
pkg/registry/secret/doc.go
Normal file
19
pkg/registry/secret/doc.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. 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 secrets provides Registry interface and its REST
|
||||||
|
// implementation for storing Secret api objects.
|
||||||
|
package secret
|
48
pkg/registry/secret/registry.go
Normal file
48
pkg/registry/secret/registry.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. 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 secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
|
||||||
|
etcdgeneric "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic/etcd"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
|
)
|
||||||
|
|
||||||
|
// registry implements custom changes to generic.Etcd.
|
||||||
|
type registry struct {
|
||||||
|
*etcdgeneric.Etcd
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEtcdRegistry returns a registry which will store Secret in the given helper
|
||||||
|
func NewEtcdRegistry(h tools.EtcdHelper) generic.Registry {
|
||||||
|
return registry{
|
||||||
|
Etcd: &etcdgeneric.Etcd{
|
||||||
|
NewFunc: func() runtime.Object { return &api.Secret{} },
|
||||||
|
NewListFunc: func() runtime.Object { return &api.SecretList{} },
|
||||||
|
EndpointName: "secrets",
|
||||||
|
KeyRootFunc: func(ctx api.Context) string {
|
||||||
|
return etcdgeneric.NamespaceKeyRootFunc(ctx, "/registry/secrets")
|
||||||
|
},
|
||||||
|
KeyFunc: func(ctx api.Context, id string) (string, error) {
|
||||||
|
return etcdgeneric.NamespaceKeyFunc(ctx, "/registry/secrets", id)
|
||||||
|
},
|
||||||
|
Helper: h,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
108
pkg/registry/secret/registry_test.go
Normal file
108
pkg/registry/secret/registry_test.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. 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 secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
|
||||||
|
etcdgeneric "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic/etcd"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
|
||||||
|
"github.com/coreos/go-etcd/etcd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTestSecretEtcdRegistry(t *testing.T) (*tools.FakeEtcdClient, generic.Registry) {
|
||||||
|
f := tools.NewFakeEtcdClient(t)
|
||||||
|
f.TestIndex = true
|
||||||
|
h := tools.EtcdHelper{f, testapi.Codec(), tools.RuntimeVersionAdapter{testapi.MetadataAccessor()}}
|
||||||
|
return f, NewEtcdRegistry(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecretCreate(t *testing.T) {
|
||||||
|
secret := &api.Secret{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "abc",
|
||||||
|
Namespace: "foo",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"data-1": []byte("value-1"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeWithSecret := tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Value: runtime.EncodeOrDie(testapi.Codec(), secret),
|
||||||
|
ModifiedIndex: 1,
|
||||||
|
CreatedIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
E: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyNode := tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{},
|
||||||
|
E: tools.EtcdErrorNotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
key := "foo"
|
||||||
|
path, err := etcdgeneric.NamespaceKeyFunc(ctx, "/registry/secrets", key)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
table := map[string]struct {
|
||||||
|
existing tools.EtcdResponseWithError
|
||||||
|
expect tools.EtcdResponseWithError
|
||||||
|
toCreate runtime.Object
|
||||||
|
errOK func(error) bool
|
||||||
|
}{
|
||||||
|
"normal": {
|
||||||
|
existing: emptyNode,
|
||||||
|
expect: nodeWithSecret,
|
||||||
|
toCreate: secret,
|
||||||
|
errOK: func(err error) bool { return err == nil },
|
||||||
|
},
|
||||||
|
"preExisting": {
|
||||||
|
existing: nodeWithSecret,
|
||||||
|
expect: nodeWithSecret,
|
||||||
|
toCreate: secret,
|
||||||
|
errOK: errors.IsAlreadyExists,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, item := range table {
|
||||||
|
fakeClient, registry := NewTestSecretEtcdRegistry(t)
|
||||||
|
fakeClient.Data[path] = item.existing
|
||||||
|
err := registry.CreateWithName(ctx, key, item.toCreate)
|
||||||
|
if !item.errOK(err) {
|
||||||
|
t.Errorf("%v: unexpected error: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e, a := item.expect, fakeClient.Data[path]; !reflect.DeepEqual(e, a) {
|
||||||
|
t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
152
pkg/registry/secret/rest.go
Normal file
152
pkg/registry/secret/rest.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. 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 secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
|
"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"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// REST provides the RESTStorage access patterns to work with Secret objects.
|
||||||
|
type REST struct {
|
||||||
|
registry generic.Registry
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewREST returns a new REST. You must use a registry created by
|
||||||
|
// NewEtcdRegistry unless you're testing.
|
||||||
|
func NewREST(registry generic.Registry) *REST {
|
||||||
|
return &REST{
|
||||||
|
registry: registry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a Secret object
|
||||||
|
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
|
||||||
|
secret, ok := obj.(*api.Secret)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid object type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !api.ValidNamespace(ctx, &secret.ObjectMeta) {
|
||||||
|
return nil, errors.NewConflict("secret", secret.Namespace, fmt.Errorf("Secret.Namespace does not match the provided context"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(secret.Name) == 0 {
|
||||||
|
secret.Name = string(util.NewUUID())
|
||||||
|
}
|
||||||
|
|
||||||
|
if errs := validation.ValidateSecret(secret); len(errs) > 0 {
|
||||||
|
return nil, errors.NewInvalid("secret", secret.Name, errs)
|
||||||
|
}
|
||||||
|
api.FillObjectMetaSystemFields(ctx, &secret.ObjectMeta)
|
||||||
|
|
||||||
|
err := rs.registry.CreateWithName(ctx, secret.Name, secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rs.registry.Get(ctx, secret.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates a Secret object.
|
||||||
|
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
|
||||||
|
secret, ok := obj.(*api.Secret)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("not a secret: %#v", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !api.ValidNamespace(ctx, &secret.ObjectMeta) {
|
||||||
|
return nil, errors.NewConflict("secret", secret.Namespace, fmt.Errorf("Secret.Namespace does not match the provided context"))
|
||||||
|
}
|
||||||
|
|
||||||
|
oldObj, err := rs.registry.Get(ctx, secret.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
editSecret := oldObj.(*api.Secret)
|
||||||
|
|
||||||
|
// set the editable fields on the existing object
|
||||||
|
editSecret.Labels = secret.Labels
|
||||||
|
editSecret.ResourceVersion = secret.ResourceVersion
|
||||||
|
editSecret.Annotations = secret.Annotations
|
||||||
|
|
||||||
|
if errs := validation.ValidateSecret(editSecret); len(errs) > 0 {
|
||||||
|
return nil, errors.NewInvalid("secret", editSecret.Name, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rs.registry.UpdateWithName(ctx, editSecret.Name, editSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rs.registry.Get(ctx, editSecret.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the Secret with the specified name
|
||||||
|
func (rs *REST) Delete(ctx api.Context, name string) (runtime.Object, error) {
|
||||||
|
obj, err := rs.registry.Get(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, ok := obj.(*api.Secret)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid object type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs.registry.Delete(ctx, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets a Secret with the specified name
|
||||||
|
func (rs *REST) Get(ctx api.Context, name string) (runtime.Object, error) {
|
||||||
|
obj, err := rs.registry.Get(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
secret, ok := obj.(*api.Secret)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid object type")
|
||||||
|
}
|
||||||
|
return secret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *REST) getAttrs(obj runtime.Object) (objLabels, objFields labels.Set, err error) {
|
||||||
|
return labels.Set{}, labels.Set{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *REST) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) {
|
||||||
|
return rs.registry.List(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) {
|
||||||
|
return rs.registry.Watch(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new api.Secret
|
||||||
|
func (*REST) New() runtime.Object {
|
||||||
|
return &api.Secret{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*REST) NewList() runtime.Object {
|
||||||
|
return &api.SecretList{}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user