Пример #1
0
// createTokenSecret creates a token secret for a given service account.  Returns the name of the token
func (e *DockercfgController) createTokenSecret(serviceAccount *api.ServiceAccount) (*api.Secret, bool, error) {
	pendingTokenName := serviceAccount.Annotations[PendingTokenAnnotation]

	// If this service account has no record of a pending token name, record one
	if len(pendingTokenName) == 0 {
		pendingTokenName = secret.Strategy.GenerateName(osautil.GetTokenSecretNamePrefix(serviceAccount))
		if serviceAccount.Annotations == nil {
			serviceAccount.Annotations = map[string]string{}
		}
		serviceAccount.Annotations[PendingTokenAnnotation] = pendingTokenName
		updatedServiceAccount, err := e.client.Core().ServiceAccounts(serviceAccount.Namespace).Update(serviceAccount)
		// Conflicts mean we'll get called to sync this service account again
		if kapierrors.IsConflict(err) {
			return nil, false, nil
		}
		if err != nil {
			return nil, false, err
		}
		serviceAccount = updatedServiceAccount
	}

	// Return the token from cache
	existingTokenSecretObj, exists, err := e.secretCache.GetByKey(serviceAccount.Namespace + "/" + pendingTokenName)
	if err != nil {
		return nil, false, err
	}
	if exists {
		existingTokenSecret := existingTokenSecretObj.(*api.Secret)
		return existingTokenSecret, len(existingTokenSecret.Data[api.ServiceAccountTokenKey]) > 0, nil
	}

	// Try to create the named pending token
	tokenSecret := &api.Secret{
		ObjectMeta: api.ObjectMeta{
			Name:      pendingTokenName,
			Namespace: serviceAccount.Namespace,
			Annotations: map[string]string{
				api.ServiceAccountNameKey: serviceAccount.Name,
				api.ServiceAccountUIDKey:  string(serviceAccount.UID),
				api.CreatedByAnnotation:   CreateDockercfgSecretsController,
			},
		},
		Type: api.SecretTypeServiceAccountToken,
		Data: map[string][]byte{},
	}

	glog.V(4).Infof("Creating token secret %q for service account %s/%s", tokenSecret.Name, serviceAccount.Namespace, serviceAccount.Name)
	token, err := e.client.Core().Secrets(tokenSecret.Namespace).Create(tokenSecret)
	// Already exists but not in cache means we'll get an add watch event and resync
	if kapierrors.IsAlreadyExists(err) {
		return nil, false, nil
	}
	if err != nil {
		return nil, false, err
	}
	return token, len(token.Data[api.ServiceAccountTokenKey]) > 0, nil
}
Пример #2
0
// createTokenSecret creates a token secret for a given service account.  Returns the name of the token
func (e *DockercfgController) createTokenSecret(serviceAccount *api.ServiceAccount) (*api.Secret, error) {
	tokenSecret := &api.Secret{
		ObjectMeta: api.ObjectMeta{
			Name:      secret.Strategy.GenerateName(osautil.GetTokenSecretNamePrefix(serviceAccount)),
			Namespace: serviceAccount.Namespace,
			Annotations: map[string]string{
				api.ServiceAccountNameKey: serviceAccount.Name,
				api.ServiceAccountUIDKey:  string(serviceAccount.UID),
			},
		},
		Type: api.SecretTypeServiceAccountToken,
		Data: map[string][]byte{},
	}

	_, err := e.client.Secrets(tokenSecret.Namespace).Create(tokenSecret)
	if err != nil {
		return nil, err
	}

	// now we have to wait for the service account token controller to make this valid
	// TODO remove this once we have a create-token endpoint
	for i := 0; i <= tokenSecretWaitTimes; i++ {
		liveTokenSecret, err := e.client.Secrets(tokenSecret.Namespace).Get(tokenSecret.Name)
		if err != nil {
			return nil, err
		}

		if len(liveTokenSecret.Data[api.ServiceAccountTokenKey]) > 0 {
			return liveTokenSecret, nil
		}

		time.Sleep(wait.Jitter(tokenSecretWaitInterval, 0.0))

	}

	// the token wasn't ever created, attempt deletion
	glog.Warningf("Deleting unfilled token secret %s/%s", tokenSecret.Namespace, tokenSecret.Name)
	if err := e.client.Secrets(tokenSecret.Namespace).Delete(tokenSecret.Name); (err != nil) && !kapierrors.IsNotFound(err) {
		util.HandleError(err)
	}
	return nil, fmt.Errorf("token never generated for %s", tokenSecret.Name)
}
Пример #3
0
// Run creates a new token secret, waits for the service account token controller to fulfill it, then adds the token to the service account
func (o *NewServiceAccountTokenOptions) Run() error {
	serviceAccount, err := o.SAClient.Get(o.SAName)
	if err != nil {
		return err
	}

	tokenSecret := &api.Secret{
		ObjectMeta: api.ObjectMeta{
			GenerateName: osautil.GetTokenSecretNamePrefix(serviceAccount),
			Namespace:    serviceAccount.Namespace,
			Labels:       o.Labels,
			Annotations: map[string]string{
				api.ServiceAccountNameKey: serviceAccount.Name,
			},
		},
		Type: api.SecretTypeServiceAccountToken,
		Data: map[string][]byte{},
	}

	persistedToken, err := o.SecretsClient.Create(tokenSecret)
	if err != nil {
		return err
	}

	// we need to wait for the service account token controller to make the new token valid
	tokenSecret, err = waitForToken(persistedToken, serviceAccount, o.Timeout, o.SecretsClient)
	if err != nil {
		return err
	}

	token, exists := tokenSecret.Data[api.ServiceAccountTokenKey]
	if !exists {
		return fmt.Errorf("service account token %q did not contain token data", tokenSecret.Name)
	}

	fmt.Fprintf(o.Out, string(token))
	if util.IsTerminalWriter(o.Out) {
		// pretty-print for a TTY
		fmt.Fprintf(o.Out, "\n")
	}
	return nil
}
Пример #4
0
func (e *DefaultExporter) Export(obj runtime.Object, exact bool) error {
	if meta, err := kapi.ObjectMetaFor(obj); err == nil {
		exportObjectMeta(meta, exact)
	} else {
		glog.V(4).Infof("Object of type %v does not have ObjectMeta: %v", reflect.TypeOf(obj), err)
	}
	ctx := kapi.NewContext()

	switch t := obj.(type) {
	case *kapi.Endpoints:
		endpoint.Strategy.PrepareForCreate(ctx, obj)
	case *kapi.ResourceQuota:
		resourcequota.Strategy.PrepareForCreate(ctx, obj)
	case *kapi.LimitRange:
	// TODO: this needs to be fixed
	//  limitrange.Strategy.PrepareForCreate(obj)
	case *kapi.Node:
		node.Strategy.PrepareForCreate(ctx, obj)
		if exact {
			return nil
		}
		// Nodes are the only resources that allow direct status edits, therefore
		// we clear that without exact so that the node value can be reused.
		t.Status = kapi.NodeStatus{}
	case *kapi.Namespace:
		namespace.Strategy.PrepareForCreate(ctx, obj)
	case *kapi.PersistentVolumeClaim:
		persistentvolumeclaim.Strategy.PrepareForCreate(ctx, obj)
	case *kapi.PersistentVolume:
		persistentvolume.Strategy.PrepareForCreate(ctx, obj)
	case *kapi.ReplicationController:
		controller.Strategy.PrepareForCreate(ctx, obj)
	case *kapi.Pod:
		pod.Strategy.PrepareForCreate(ctx, obj)
	case *kapi.PodTemplate:
	case *kapi.Service:
		// TODO: service does not yet have a strategy
		t.Status = kapi.ServiceStatus{}
		if exact {
			return nil
		}
		if t.Spec.ClusterIP != kapi.ClusterIPNone {
			t.Spec.ClusterIP = ""
		}
		if t.Spec.Type == kapi.ServiceTypeNodePort {
			for i := range t.Spec.Ports {
				t.Spec.Ports[i].NodePort = 0
			}
		}
	case *kapi.Secret:
		secret.Strategy.PrepareForCreate(ctx, obj)
		if exact {
			return nil
		}
		// secrets that are tied to the UID of a service account cannot be exported anyway
		if t.Type == kapi.SecretTypeServiceAccountToken || len(t.Annotations[kapi.ServiceAccountUIDKey]) > 0 {
			return ErrExportOmit
		}
	case *kapi.ServiceAccount:
		serviceaccount.Strategy.PrepareForCreate(ctx, obj)
		if exact {
			return nil
		}

		dockercfgSecretPrefix := osautil.GetDockercfgSecretNamePrefix(t)
		newImagePullSecrets := []kapi.LocalObjectReference{}
		for _, secretRef := range t.ImagePullSecrets {
			if strings.HasPrefix(secretRef.Name, dockercfgSecretPrefix) {
				continue
			}
			newImagePullSecrets = append(newImagePullSecrets, secretRef)
		}
		t.ImagePullSecrets = newImagePullSecrets

		tokenSecretPrefix := osautil.GetTokenSecretNamePrefix(t)
		newMountableSecrets := []kapi.ObjectReference{}
		for _, secretRef := range t.Secrets {
			if strings.HasPrefix(secretRef.Name, dockercfgSecretPrefix) ||
				strings.HasPrefix(secretRef.Name, tokenSecretPrefix) {
				continue
			}
			newMountableSecrets = append(newMountableSecrets, secretRef)
		}
		t.Secrets = newMountableSecrets

	case *deployapi.DeploymentConfig:
		return deployrest.Strategy.Export(ctx, obj, exact)

	case *buildapi.BuildConfig:
		buildconfigrest.Strategy.PrepareForCreate(ctx, obj)
		// TODO: should be handled by prepare for create
		t.Status.LastVersion = 0
		for i := range t.Spec.Triggers {
			if p := t.Spec.Triggers[i].ImageChange; p != nil {
				p.LastTriggeredImageID = ""
			}
		}
	case *buildapi.Build:
		buildrest.Strategy.PrepareForCreate(ctx, obj)
		// TODO: should be handled by prepare for create
		t.Status.Duration = 0
		t.Status.Phase = buildapi.BuildPhaseNew
		t.Status.StartTimestamp = nil
		t.Status.CompletionTimestamp = nil
		if exact {
			return nil
		}
		if t.Status.Config != nil {
			t.Status.Config = &kapi.ObjectReference{Name: t.Status.Config.Name}
		}
	case *routeapi.Route:
	case *imageapi.Image:
	case *imageapi.ImageStream:
		if exact {
			return nil
		}
		// if we point to a docker image repository upstream, copy only the spec tags
		if len(t.Spec.DockerImageRepository) > 0 {
			t.Status = imageapi.ImageStreamStatus{}
			break
		}
		// create an image stream that mirrors (each spec tag points to the remote image stream)
		if len(t.Status.DockerImageRepository) > 0 {
			ref, err := imageapi.ParseDockerImageReference(t.Status.DockerImageRepository)
			if err != nil {
				return err
			}
			newSpec := imageapi.ImageStreamSpec{
				Tags: map[string]imageapi.TagReference{},
			}
			for name, tag := range t.Status.Tags {
				if len(tag.Items) > 0 {
					// copy annotations
					existing := t.Spec.Tags[name]
					// point directly to that registry
					ref.Tag = name
					existing.From = &kapi.ObjectReference{
						Kind: "DockerImage",
						Name: ref.String(),
					}
					newSpec.Tags[name] = existing
				}
			}
			for name, ref := range t.Spec.Tags {
				if _, ok := t.Status.Tags[name]; ok {
					continue
				}
				// TODO: potentially trim some of these
				newSpec.Tags[name] = ref
			}
			t.Spec = newSpec
			t.Status = imageapi.ImageStreamStatus{
				Tags: map[string]imageapi.TagEventList{},
			}
			break
		}

		// otherwise, try to snapshot the most recent image as spec items
		newSpec := imageapi.ImageStreamSpec{
			Tags: map[string]imageapi.TagReference{},
		}
		for name, tag := range t.Status.Tags {
			if len(tag.Items) > 0 {
				// copy annotations
				existing := t.Spec.Tags[name]
				existing.From = &kapi.ObjectReference{
					Kind: "DockerImage",
					Name: tag.Items[0].DockerImageReference,
				}
				newSpec.Tags[name] = existing
			}
		}
		t.Spec = newSpec
		t.Status = imageapi.ImageStreamStatus{
			Tags: map[string]imageapi.TagEventList{},
		}

	case *imageapi.ImageStreamTag:
		exportObjectMeta(&t.Image.ObjectMeta, exact)
	case *imageapi.ImageStreamImage:
		exportObjectMeta(&t.Image.ObjectMeta, exact)

	default:
		glog.V(4).Infof("No export strategy defined for objects of type %v", reflect.TypeOf(obj))
	}
	return nil
}
Пример #5
0
func TestExport(t *testing.T) {
	exporter := &defaultExporter{}

	baseSA := &kapi.ServiceAccount{}
	baseSA.Name = "my-sa"

	tests := []struct {
		name        string
		object      runtime.Object
		exact       bool
		expectedObj runtime.Object
		expectedErr error
	}{
		{
			name:   "export deploymentConfig",
			object: deploytest.OkDeploymentConfig(1),
			expectedObj: &deployapi.DeploymentConfig{
				ObjectMeta: kapi.ObjectMeta{
					Name: "config",
				},
				LatestVersion: 0,
				Triggers: []deployapi.DeploymentTriggerPolicy{
					deploytest.OkImageChangeTrigger(),
				},
				Template: deploytest.OkDeploymentTemplate(),
			},
			expectedErr: nil,
		},
		{
			name: "export imageStream",
			object: &imageapi.ImageStream{
				ObjectMeta: kapi.ObjectMeta{
					Name:      "test",
					Namespace: "other",
				},
				Spec: imageapi.ImageStreamSpec{
					Tags: map[string]imageapi.TagReference{
						"v1": {
							Annotations: map[string]string{"an": "annotation"},
						},
					},
				},
				Status: imageapi.ImageStreamStatus{
					DockerImageRepository: "foo/bar",
					Tags: map[string]imageapi.TagEventList{
						"v1": {
							Items: []imageapi.TagEvent{{Image: "the image"}},
						},
					},
				},
			},
			expectedObj: &imageapi.ImageStream{
				ObjectMeta: kapi.ObjectMeta{
					Name:      "test",
					Namespace: "",
				},
				Spec: imageapi.ImageStreamSpec{
					Tags: map[string]imageapi.TagReference{
						"v1": {
							From: &kapi.ObjectReference{
								Kind: "DockerImage",
								Name: "foo/bar:v1",
							},
							Annotations: map[string]string{"an": "annotation"},
						},
					},
				},
				Status: imageapi.ImageStreamStatus{
					Tags: map[string]imageapi.TagEventList{},
				},
			},
			expectedErr: nil,
		},
		{
			name: "remove unexportable SA secrets",
			object: &kapi.ServiceAccount{
				ObjectMeta: kapi.ObjectMeta{
					Name: baseSA.Name,
				},
				ImagePullSecrets: []kapi.LocalObjectReference{
					{Name: osautil.GetDockercfgSecretNamePrefix(baseSA) + "-foo"},
					{Name: "another-pull-secret"},
				},
				Secrets: []kapi.ObjectReference{
					{Name: osautil.GetDockercfgSecretNamePrefix(baseSA) + "-foo"},
					{Name: osautil.GetTokenSecretNamePrefix(baseSA) + "-foo"},
					{Name: "another-mountable-secret"},
				},
			},
			expectedObj: &kapi.ServiceAccount{
				ObjectMeta: kapi.ObjectMeta{
					Name: baseSA.Name,
				},
				ImagePullSecrets: []kapi.LocalObjectReference{
					{Name: "another-pull-secret"},
				},
				Secrets: []kapi.ObjectReference{
					{Name: "another-mountable-secret"},
				},
			},
			expectedErr: nil,
		},
		{
			name: "do not remove unexportable SA secrets with exact",
			object: &kapi.ServiceAccount{
				ObjectMeta: kapi.ObjectMeta{
					Name: baseSA.Name,
				},
				ImagePullSecrets: []kapi.LocalObjectReference{
					{Name: osautil.GetDockercfgSecretNamePrefix(baseSA) + "-foo"},
					{Name: "another-pull-secret"},
				},
				Secrets: []kapi.ObjectReference{
					{Name: osautil.GetDockercfgSecretNamePrefix(baseSA) + "-foo"},
					{Name: osautil.GetTokenSecretNamePrefix(baseSA) + "-foo"},
					{Name: "another-mountable-secret"},
				},
			},
			expectedObj: &kapi.ServiceAccount{
				ObjectMeta: kapi.ObjectMeta{
					Name: baseSA.Name,
				},
				ImagePullSecrets: []kapi.LocalObjectReference{
					{Name: osautil.GetDockercfgSecretNamePrefix(baseSA) + "-foo"},
					{Name: "another-pull-secret"},
				},
				Secrets: []kapi.ObjectReference{
					{Name: osautil.GetDockercfgSecretNamePrefix(baseSA) + "-foo"},
					{Name: osautil.GetTokenSecretNamePrefix(baseSA) + "-foo"},
					{Name: "another-mountable-secret"},
				},
			},
			exact:       true,
			expectedErr: nil,
		},
	}

	for _, test := range tests {
		if err := exporter.Export(test.object, test.exact); err != test.expectedErr {
			t.Errorf("error mismatch: expected %v, got %v", test.expectedErr, err)
		}

		if !reflect.DeepEqual(test.object, test.expectedObj) {
			t.Errorf("object mismatch: expected \n%v\ngot \n%v\n", test.expectedObj, test.object)
		}
	}
}