package secrets
import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
kapi "k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
client "k8s.io/kubernetes/pkg/client/unversioned"
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/sets"
)
// SecretOptions Structure holding state for processing secret linking and
// unlinking.
type SecretOptions struct {
TargetName string
SecretNames []string
typeFlags []string
Namespace string
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
ClientMapper resource.ClientMapper
ClientInterface client.Interface
Out io.Writer
}
// Complete Parses the command line arguments and populates SecretOptions
func (o *SecretOptions) Complete(f *kcmdutil.Factory, args []string) error {
if len(args) < 2 {
return errors.New("must have service account name and at least one secret name")
}
o.TargetName = args[0]
o.SecretNames = args[1:]
var err error
o.ClientInterface, err = f.Client()
if err != nil {
return err
}
o.Namespace, _, err = f.DefaultNamespace()
if err != nil {
return err
}
o.Mapper, o.Typer = f.Object(false)
o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping)
return nil
}
// Validate Ensures that all arguments have appropriate values
func (o SecretOptions) Validate() error {
if len(o.TargetName) == 0 {
return errors.New("service account name must be present")
}
if len(o.SecretNames) == 0 {
return errors.New("secret name must be present")
}
if o.Mapper == nil {
return errors.New("Mapper must be present")
}
if o.Typer == nil {
return errors.New("Typer must be present")
}
if o.ClientMapper == nil {
return errors.New("ClientMapper must be present")
}
if o.ClientInterface == nil {
return errors.New("ClientInterface must be present")
}
return nil
}
// GetServiceAccount Retrieve the service account object specified by the command
func (o SecretOptions) GetServiceAccount() (*kapi.ServiceAccount, error) {
r := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper, kapi.Codecs.UniversalDecoder()).
NamespaceParam(o.Namespace).
ResourceNames("serviceaccounts", o.TargetName).
SingleResourceType().
Do()
if r.Err() != nil {
return nil, r.Err()
}
obj, err := r.Object()
if err != nil {
return nil, err
}
switch t := obj.(type) {
case *kapi.ServiceAccount:
return t, nil
default:
return nil, fmt.Errorf("unhandled object: %#v", t)
}
}
// GetSecretNames Get a list of the names of the secrets in a set of them
func (o SecretOptions) GetSecretNames(secrets []*kapi.Secret) sets.String {
names := sets.String{}
for _, secret := range secrets {
names.Insert(secret.Name)
}
return names
}
// GetMountSecretNames Get a list of the names of the mount secrets associated
// with a service account
func (o SecretOptions) GetMountSecretNames(serviceaccount *kapi.ServiceAccount) sets.String {
names := sets.String{}
for _, secret := range serviceaccount.Secrets {
names.Insert(secret.Name)
}
return names
}
// GetPullSecretNames Get a list of the names of the pull secrets associated
// with a service account.
func (o SecretOptions) GetPullSecretNames(serviceaccount *kapi.ServiceAccount) sets.String {
names := sets.String{}
for _, secret := range serviceaccount.ImagePullSecrets {
names.Insert(secret.Name)
}
return names
}
// GetOut Retrieve the output writer
func (o SecretOptions) GetOut() io.Writer {
if o.Out == nil {
return ioutil.Discard
}
return o.Out
}
// GetSecrets Return a list of secret objects in the default namespace
// If allowNonExisting is set to true, we will return the non-existing secrets as well.
func (o SecretOptions) GetSecrets(allowNonExisting bool) ([]*kapi.Secret, bool, error) {
secrets := []*kapi.Secret{}
hasNotFound := false
for _, secretName := range o.SecretNames {
r := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper, kapi.Codecs.UniversalDecoder()).
NamespaceParam(o.Namespace).
ResourceNames("secrets", secretName).
SingleResourceType().
Do()
if r.Err() != nil {
return nil, false, r.Err()
}
obj, err := r.Object()
if err != nil {
// If the secret is not found it means it was deleted but we want still to allow to
// unlink a removed secret from the service account
if kerrors.IsNotFound(err) {
fmt.Fprintf(os.Stderr, "secret %q not found\n", secretName)
hasNotFound = true
if allowNonExisting {
obj = &kapi.Secret{
ObjectMeta: kapi.ObjectMeta{
Name: secretName,
},
}
} else {
continue
}
} else if err != nil {
return nil, false, err
}
}
switch t := obj.(type) {
case *kapi.Secret:
secrets = append(secrets, t)
default:
return nil, false, fmt.Errorf("unhandled object: %#v", t)
}
}
if len(secrets) == 0 {
return nil, false, errors.New("No valid secrets found")
}
return secrets, hasNotFound, nil
}