Example #1
0
// UpdateSecret updates a Secret referenced by SecretID with the given SecretSpec.
// - Returns `NotFound` if the Secret is not found.
// - Returns `InvalidArgument` if the SecretSpec is malformed or anything other than Labels is changed
// - Returns an error if the update fails.
func (s *Server) UpdateSecret(ctx context.Context, request *api.UpdateSecretRequest) (*api.UpdateSecretResponse, error) {
	if request.SecretID == "" || request.SecretVersion == nil {
		return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
	}

	var secret *api.Secret
	err := s.store.Update(func(tx store.Tx) error {
		secret = store.GetSecret(tx, request.SecretID)
		if secret == nil {
			return nil
		}

		if secret.Spec.Annotations.Name != request.Spec.Annotations.Name || request.Spec.Data != nil {
			return grpc.Errorf(codes.InvalidArgument, "only updates to Labels are allowed")
		}

		// We only allow updating Labels
		secret.Meta.Version = *request.SecretVersion
		secret.Spec.Annotations.Labels = request.Spec.Annotations.Labels

		return store.UpdateSecret(tx, secret)
	})
	if err != nil {
		return nil, err
	}
	if secret == nil {
		return nil, grpc.Errorf(codes.NotFound, "secret %s not found", request.SecretID)
	}

	// WARN: we should never return the actual secret data here. We need to redact the private fields first.
	secret.Spec.Data = nil
	return &api.UpdateSecretResponse{
		Secret: secret,
	}, nil
}
Example #2
0
// UpdateSecret updates a Secret referenced by SecretID with the given SecretSpec.
// - Returns `NotFound` if the Secret is not found.
// - Returns `InvalidArgument` if the SecretSpec is malformed or anything other than Labels is changed
// - Returns an error if the update fails.
func (s *Server) UpdateSecret(ctx context.Context, request *api.UpdateSecretRequest) (*api.UpdateSecretResponse, error) {
	if request.SecretID == "" || request.SecretVersion == nil {
		return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
	}

	var secret *api.Secret
	err := s.store.Update(func(tx store.Tx) error {
		secret = store.GetSecret(tx, request.SecretID)
		if secret == nil {
			return nil
		}

		// Check if the Name is different than the current name, or the secret is non-nil and different
		// than the current secret
		if secret.Spec.Annotations.Name != request.Spec.Annotations.Name ||
			(request.Spec.Data != nil && subtle.ConstantTimeCompare(request.Spec.Data, secret.Spec.Data) == 0) {
			return grpc.Errorf(codes.InvalidArgument, "only updates to Labels are allowed")
		}

		// We only allow updating Labels
		secret.Meta.Version = *request.SecretVersion
		secret.Spec.Annotations.Labels = request.Spec.Annotations.Labels

		return store.UpdateSecret(tx, secret)
	})
	if err != nil {
		return nil, err
	}
	if secret == nil {
		return nil, grpc.Errorf(codes.NotFound, "secret %s not found", request.SecretID)
	}

	log.G(ctx).WithFields(logrus.Fields{
		"secret.ID":   request.SecretID,
		"secret.Name": request.Spec.Annotations.Name,
		"method":      "UpdateSecret",
	}).Debugf("secret updated")

	// WARN: we should never return the actual secret data here. We need to redact the private fields first.
	secret.Spec.Data = nil
	return &api.UpdateSecretResponse{
		Secret: secret,
	}, nil
}
Example #3
0
// If a secret is updated or deleted, even if it's for an existing task, no changes will be sent down
func TestAssignmentsSecretUpdateAndDeletion(t *testing.T) {
	t.Parallel()

	gd, err := startDispatcher(DefaultConfig())
	assert.NoError(t, err)
	defer gd.Close()

	expectedSessionID, nodeID := getSessionAndNodeID(t, gd.Clients[0])

	// create the relevant secrets and tasks
	secrets, tasks := makeTasksAndSecrets(t, nodeID)
	err = gd.Store.Update(func(tx store.Tx) error {
		for _, secret := range secrets[:len(secrets)-1] {
			assert.NoError(t, store.CreateSecret(tx, secret))
		}
		for _, task := range tasks {
			assert.NoError(t, store.CreateTask(tx, task))
		}
		return nil
	})
	assert.NoError(t, err)

	stream, err := gd.Clients[0].Assignments(context.Background(), &api.AssignmentsRequest{SessionID: expectedSessionID})
	assert.NoError(t, err)
	defer stream.CloseSend()

	time.Sleep(100 * time.Millisecond)

	// check the initial task and secret stream
	resp, err := stream.Recv()
	assert.NoError(t, err)

	// FIXME(aaronl): This is hard to maintain.
	assert.Equal(t, 16, len(resp.Changes))
	taskChanges, secretChanges := collectTasksAndSecrets(resp.Changes)
	assert.Len(t, taskChanges, 10) // 10 types of task states >= assigned, 2 types < assigned
	for _, task := range tasks[2:] {
		assert.NotNil(t, taskChanges[idAndAction{id: task.ID, action: api.AssignmentChange_AssignmentActionUpdate}])
	}
	assert.Len(t, secretChanges, 6) // 6 types of task states between assigned and running inclusive
	for _, secret := range secrets[2:8] {
		assert.NotNil(t, secretChanges[idAndAction{id: secret.ID, action: api.AssignmentChange_AssignmentActionUpdate}])
	}

	// updating secrets, used by tasks or not, do not cause any changes
	err = gd.Store.Update(func(tx store.Tx) error {
		for _, secret := range secrets[:len(secrets)-2] {
			secret.Spec.Data = []byte("new secret data")
			assert.NoError(t, store.UpdateSecret(tx, secret))
		}
		return nil
	})
	assert.NoError(t, err)

	recvChan := make(chan struct{})
	go func() {
		_, _ = stream.Recv()
		recvChan <- struct{}{}
	}()

	select {
	case <-recvChan:
		assert.Fail(t, "secret update should not trigger dispatcher update")
	case <-time.After(250 * time.Millisecond):
	}

	// deleting secrets, used by tasks or not, do not cause any changes
	err = gd.Store.Update(func(tx store.Tx) error {
		for _, secret := range secrets[:len(secrets)-2] {
			assert.NoError(t, store.DeleteSecret(tx, secret.ID))
		}
		return nil
	})
	assert.NoError(t, err)

	select {
	case <-recvChan:
		assert.Fail(t, "secret delete should not trigger dispatcher update")
	case <-time.After(250 * time.Millisecond):
	}
}