package secrets
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"strings"
"github.com/openshift/origin/pkg/cmd/templates"
"k8s.io/kubernetes/pkg/api"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/credentialprovider"
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)
const CreateDockerConfigSecretRecommendedName = "new-dockercfg"
var (
createDockercfgLong = templates.LongDesc(`
Create a new dockercfg secret
Dockercfg secrets are used to authenticate against Docker registries.
When using the Docker command line to push images, you can authenticate to a given registry by running
'docker login DOCKER_REGISTRY_SERVER --username=DOCKER_USER --password=DOCKER_PASSWORD --email=DOCKER_EMAIL'.
That produces a ~/.dockercfg file that is used by subsequent 'docker push' and 'docker pull' commands to
authenticate to the registry.
When creating applications, you may have a Docker registry that requires authentication. In order for the
nodes to pull images on your behalf, they have to have the credentials. You can provide this information
by creating a dockercfg secret and attaching it to your service account.`)
createDockercfgExample = templates.Examples(`
# Create a new .dockercfg secret:
%[1]s SECRET --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
# Create a new .dockercfg secret from an existing file:
%[2]s SECRET path/to/.dockercfg
# Create a new .docker/config.json secret from an existing file:
%[2]s SECRET .dockerconfigjson=path/to/.docker/config.json
# To add new secret to 'imagePullSecrets' for the node, or 'secrets' for builds, use:
%[3]s SERVICE_ACCOUNT`)
)
type CreateDockerConfigOptions struct {
SecretName string
RegistryLocation string
Username string
Password string
EmailAddress string
SecretsInterface client.SecretsInterface
Out io.Writer
}
// NewCmdCreateDockerConfigSecret creates a command object for making a dockercfg secret
func NewCmdCreateDockerConfigSecret(name, fullName string, f *kcmdutil.Factory, out io.Writer, newSecretFullName, ocEditFullName string) *cobra.Command {
o := &CreateDockerConfigOptions{Out: out}
cmd := &cobra.Command{
Use: fmt.Sprintf("%s SECRET --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL", name),
Short: "Create a new dockercfg secret",
Long: createDockercfgLong,
Example: fmt.Sprintf(createDockercfgExample, fullName, newSecretFullName, ocEditFullName),
Run: func(c *cobra.Command, args []string) {
if err := o.Complete(f, args); err != nil {
kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
}
if err := o.Validate(); err != nil {
kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
}
if len(kcmdutil.GetFlagString(c, "output")) != 0 {
secret, err := o.NewDockerSecret()
kcmdutil.CheckErr(err)
mapper, _ := f.Object(false)
kcmdutil.CheckErr(f.PrintObject(c, mapper, secret, out))
return
}
if err := o.CreateDockerSecret(); err != nil {
kcmdutil.CheckErr(err)
}
},
}
cmd.Flags().StringVar(&o.Username, "docker-username", "", "Username for Docker registry authentication")
cmd.Flags().StringVar(&o.Password, "docker-password", "", "Password for Docker registry authentication")
cmd.Flags().StringVar(&o.EmailAddress, "docker-email", "", "Email for Docker registry")
cmd.Flags().StringVar(&o.RegistryLocation, "docker-server", "https://index.docker.io/v1/", "Server location for Docker registry")
kcmdutil.AddPrinterFlags(cmd)
return cmd
}
func (o CreateDockerConfigOptions) CreateDockerSecret() error {
secret, err := o.NewDockerSecret()
if err != nil {
return err
}
if _, err := o.SecretsInterface.Create(secret); err != nil {
return err
}
fmt.Fprintf(o.GetOut(), "secret/%s\n", secret.Name)
return nil
}
func (o CreateDockerConfigOptions) NewDockerSecret() (*api.Secret, error) {
dockercfgAuth := credentialprovider.DockerConfigEntry{
Username: o.Username,
Password: o.Password,
Email: o.EmailAddress,
}
dockerCfg := map[string]credentialprovider.DockerConfigEntry{o.RegistryLocation: dockercfgAuth}
dockercfgContent, err := json.Marshal(dockerCfg)
if err != nil {
return nil, err
}
secret := &api.Secret{}
secret.Name = o.SecretName
secret.Type = api.SecretTypeDockercfg
secret.Data = map[string][]byte{}
secret.Data[api.DockerConfigKey] = dockercfgContent
return secret, nil
}
func (o *CreateDockerConfigOptions) Complete(f *kcmdutil.Factory, args []string) error {
if len(args) != 1 {
return errors.New("must have exactly one argument: secret name")
}
o.SecretName = args[0]
client, err := f.Client()
if err != nil {
return err
}
namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}
o.SecretsInterface = client.Secrets(namespace)
return nil
}
func (o CreateDockerConfigOptions) Validate() error {
if len(o.SecretName) == 0 {
return errors.New("secret name must be present")
}
if len(o.RegistryLocation) == 0 {
return errors.New("docker-server must be present")
}
if len(o.Username) == 0 {
return errors.New("docker-username must be present")
}
if len(o.Password) == 0 {
return errors.New("docker-password must be present")
}
if len(o.EmailAddress) == 0 {
return errors.New("docker-email must be present")
}
if o.SecretsInterface == nil {
return errors.New("secrets interface must be present")
}
if strings.Contains(o.Username, ":") {
return fmt.Errorf("username '%v' is illegal because it contains a ':'", o.Username)
}
return nil
}
func (o CreateDockerConfigOptions) GetOut() io.Writer {
if o.Out == nil {
return ioutil.Discard
}
return o.Out
}