secret_for_docker_registry.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package versioned
  14. import (
  15. "encoding/base64"
  16. "encoding/json"
  17. "fmt"
  18. "k8s.io/api/core/v1"
  19. "k8s.io/apimachinery/pkg/runtime"
  20. "k8s.io/kubernetes/pkg/kubectl/generate"
  21. "k8s.io/kubernetes/pkg/kubectl/util/hash"
  22. )
  23. // SecretForDockerRegistryGeneratorV1 supports stable generation of a docker registry secret
  24. type SecretForDockerRegistryGeneratorV1 struct {
  25. // Name of secret (required)
  26. Name string
  27. // FileSources to derive the secret from (optional)
  28. FileSources []string
  29. // Username for registry (required)
  30. Username string
  31. // Email for registry (optional)
  32. Email string
  33. // Password for registry (required)
  34. Password string
  35. // Server for registry (required)
  36. Server string
  37. // AppendHash; if true, derive a hash from the Secret and append it to the name
  38. AppendHash bool
  39. }
  40. // Ensure it supports the generator pattern that uses parameter injection
  41. var _ generate.Generator = &SecretForDockerRegistryGeneratorV1{}
  42. // Ensure it supports the generator pattern that uses parameters specified during construction
  43. var _ generate.StructuredGenerator = &SecretForDockerRegistryGeneratorV1{}
  44. // Generate returns a secret using the specified parameters
  45. func (s SecretForDockerRegistryGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
  46. err := generate.ValidateParams(s.ParamNames(), genericParams)
  47. if err != nil {
  48. return nil, err
  49. }
  50. delegate := &SecretForDockerRegistryGeneratorV1{}
  51. hashParam, found := genericParams["append-hash"]
  52. if found {
  53. hashBool, isBool := hashParam.(bool)
  54. if !isBool {
  55. return nil, fmt.Errorf("expected bool, found :%v", hashParam)
  56. }
  57. delegate.AppendHash = hashBool
  58. delete(genericParams, "append-hash")
  59. }
  60. params := map[string]string{}
  61. for key, value := range genericParams {
  62. strVal, isString := value.(string)
  63. if !isString {
  64. return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
  65. }
  66. params[key] = strVal
  67. }
  68. delegate.Name = params["name"]
  69. delegate.Username = params["docker-username"]
  70. delegate.Email = params["docker-email"]
  71. delegate.Password = params["docker-password"]
  72. delegate.Server = params["docker-server"]
  73. return delegate.StructuredGenerate()
  74. }
  75. // StructuredGenerate outputs a secret object using the configured fields
  76. func (s SecretForDockerRegistryGeneratorV1) StructuredGenerate() (runtime.Object, error) {
  77. if err := s.validate(); err != nil {
  78. return nil, err
  79. }
  80. secret := &v1.Secret{}
  81. secret.Name = s.Name
  82. secret.Type = v1.SecretTypeDockerConfigJson
  83. secret.Data = map[string][]byte{}
  84. if len(s.FileSources) > 0 {
  85. if err := handleFromFileSources(secret, s.FileSources); err != nil {
  86. return nil, err
  87. }
  88. }
  89. if len(s.FileSources) == 0 {
  90. dockercfgJSONContent, err := handleDockerCfgJSONContent(s.Username, s.Password, s.Email, s.Server)
  91. if err != nil {
  92. return nil, err
  93. }
  94. secret.Data[v1.DockerConfigJsonKey] = dockercfgJSONContent
  95. }
  96. if s.AppendHash {
  97. h, err := hash.SecretHash(secret)
  98. if err != nil {
  99. return nil, err
  100. }
  101. secret.Name = fmt.Sprintf("%s-%s", secret.Name, h)
  102. }
  103. return secret, nil
  104. }
  105. // ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
  106. func (s SecretForDockerRegistryGeneratorV1) ParamNames() []generate.GeneratorParam {
  107. return []generate.GeneratorParam{
  108. {Name: "name", Required: true},
  109. {Name: "from-file", Required: false},
  110. {Name: "docker-username", Required: true},
  111. {Name: "docker-email", Required: false},
  112. {Name: "docker-password", Required: true},
  113. {Name: "docker-server", Required: true},
  114. {Name: "append-hash", Required: false},
  115. }
  116. }
  117. // validate validates required fields are set to support structured generation
  118. func (s SecretForDockerRegistryGeneratorV1) validate() error {
  119. if len(s.Name) == 0 {
  120. return fmt.Errorf("name must be specified")
  121. }
  122. if len(s.FileSources) == 0 {
  123. if len(s.Username) == 0 {
  124. return fmt.Errorf("username must be specified")
  125. }
  126. if len(s.Password) == 0 {
  127. return fmt.Errorf("password must be specified")
  128. }
  129. if len(s.Server) == 0 {
  130. return fmt.Errorf("server must be specified")
  131. }
  132. }
  133. return nil
  134. }
  135. // handleDockerCfgJSONContent serializes a ~/.docker/config.json file
  136. func handleDockerCfgJSONContent(username, password, email, server string) ([]byte, error) {
  137. dockercfgAuth := DockerConfigEntry{
  138. Username: username,
  139. Password: password,
  140. Email: email,
  141. Auth: encodeDockerConfigFieldAuth(username, password),
  142. }
  143. dockerCfgJSON := DockerConfigJSON{
  144. Auths: map[string]DockerConfigEntry{server: dockercfgAuth},
  145. }
  146. return json.Marshal(dockerCfgJSON)
  147. }
  148. func encodeDockerConfigFieldAuth(username, password string) string {
  149. fieldValue := username + ":" + password
  150. return base64.StdEncoding.EncodeToString([]byte(fieldValue))
  151. }
  152. // DockerConfigJSON represents a local docker auth config file
  153. // for pulling images.
  154. type DockerConfigJSON struct {
  155. Auths DockerConfig `json:"auths"`
  156. // +optional
  157. HttpHeaders map[string]string `json:"HttpHeaders,omitempty"`
  158. }
  159. // DockerConfig represents the config file used by the docker CLI.
  160. // This config that represents the credentials that should be used
  161. // when pulling images from specific image repositories.
  162. type DockerConfig map[string]DockerConfigEntry
  163. type DockerConfigEntry struct {
  164. Username string `json:"username,omitempty"`
  165. Password string `json:"password,omitempty"`
  166. Email string `json:"email,omitempty"`
  167. Auth string `json:"auth,omitempty"`
  168. }