Beispiel #1
0
// checkSecretExistence finds if the secret exists
func (s *Server) checkSecretExistence(tx store.Tx, spec *api.ServiceSpec) error {
	container := spec.Task.GetContainer()
	if container == nil {
		return nil
	}

	var failedSecrets []string
	for _, secretRef := range container.Secrets {
		secret := store.GetSecret(tx, secretRef.SecretID)
		// Check to see if the secret exists and secretRef.SecretName matches the actual secretName
		if secret == nil || secret.Spec.Annotations.Name != secretRef.SecretName {
			failedSecrets = append(failedSecrets, secretRef.SecretName)
		}
	}

	if len(failedSecrets) > 0 {
		secretStr := "secrets"
		if len(failedSecrets) == 1 {
			secretStr = "secret"
		}

		return grpc.Errorf(codes.InvalidArgument, "%s not found: %v", secretStr, strings.Join(failedSecrets, ", "))

	}

	return nil
}
Beispiel #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
		}

		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
}
Beispiel #3
0
// RemoveSecret removes the secret referenced by `RemoveSecretRequest.ID`.
// - Returns `InvalidArgument` if `RemoveSecretRequest.ID` is empty.
// - Returns `NotFound` if the a secret named `RemoveSecretRequest.ID` is not found.
// - Returns `SecretInUse` if the secret is currently in use
// - Returns an error if the deletion fails.
func (s *Server) RemoveSecret(ctx context.Context, request *api.RemoveSecretRequest) (*api.RemoveSecretResponse, error) {
	if request.SecretID == "" {
		return nil, grpc.Errorf(codes.InvalidArgument, "secret ID must be provided")
	}

	err := s.store.Update(func(tx store.Tx) error {
		// Check if the secret exists
		secret := store.GetSecret(tx, request.SecretID)
		if secret == nil {
			return grpc.Errorf(codes.NotFound, "could not find secret %s", request.SecretID)
		}

		// Check if any services currently reference this secret, return error if so
		services, err := store.FindServices(tx, store.ByReferencedSecretID(request.SecretID))
		if err != nil {
			return grpc.Errorf(codes.Internal, "could not find services using secret %s: %v", request.SecretID, err)
		}

		if len(services) != 0 {
			serviceNames := make([]string, 0, len(services))
			for _, service := range services {
				serviceNames = append(serviceNames, service.Spec.Annotations.Name)
			}

			secretName := secret.Spec.Annotations.Name
			serviceNameStr := strings.Join(serviceNames, ", ")
			serviceStr := "services"
			if len(serviceNames) == 1 {
				serviceStr = "service"
			}

			return grpc.Errorf(codes.InvalidArgument, "secret '%s' is in use by the following %s: %v", secretName, serviceStr, serviceNameStr)
		}

		return store.DeleteSecret(tx, request.SecretID)
	})
	switch err {
	case store.ErrNotExist:
		return nil, grpc.Errorf(codes.NotFound, "secret %s not found", request.SecretID)
	case nil:
		log.G(ctx).WithFields(logrus.Fields{
			"secret.ID": request.SecretID,
			"method":    "RemoveSecret",
		}).Debugf("secret removed")

		return &api.RemoveSecretResponse{}, nil
	default:
		return nil, err
	}
}
Beispiel #4
0
// GetSecret returns a `GetSecretResponse` with a `Secret` with the same
// id as `GetSecretRequest.SecretID`
// - Returns `NotFound` if the Secret with the given id is not found.
// - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty.
// - Returns an error if getting fails.
func (s *Server) GetSecret(ctx context.Context, request *api.GetSecretRequest) (*api.GetSecretResponse, error) {
	if request.SecretID == "" {
		return nil, grpc.Errorf(codes.InvalidArgument, "secret ID must be provided")
	}

	var secret *api.Secret
	s.store.View(func(tx store.ReadTx) {
		secret = store.GetSecret(tx, request.SecretID)
	})

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

	return &api.GetSecretResponse{Secret: secret}, nil
}
Beispiel #5
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
}
Beispiel #6
0
func TestCreateSecret(t *testing.T) {
	ts := newTestServer(t)
	defer ts.Stop()

	// ---- creating a secret with an invalid spec fails, thus checking that CreateSecret validates the spec ----
	_, err := ts.Client.CreateSecret(context.Background(), &api.CreateSecretRequest{Spec: createSecretSpec("", nil, nil)})
	assert.Error(t, err)
	assert.Equal(t, codes.InvalidArgument, grpc.Code(err), grpc.ErrorDesc(err))

	// ---- creating a secret with a valid spec succeeds, and returns a secret that reflects the secret in the store
	// exactly, but without the private data ----
	data := []byte("secret")
	creationSpec := createSecretSpec("name", data, nil)
	validSpecRequest := api.CreateSecretRequest{Spec: creationSpec}

	resp, err := ts.Client.CreateSecret(context.Background(), &validSpecRequest)
	assert.NoError(t, err)
	assert.NotNil(t, resp)
	assert.NotNil(t, resp.Secret)

	// the data should be empty/omitted
	assert.Equal(t, *createSecretSpec("name", nil, nil), resp.Secret.Spec)

	// for sanity, check that the stored secret still has the secret data
	var storedSecret *api.Secret
	ts.Store.View(func(tx store.ReadTx) {
		storedSecret = store.GetSecret(tx, resp.Secret.ID)
	})
	assert.NotNil(t, storedSecret)
	assert.Equal(t, data, storedSecret.Spec.Data)

	// ---- creating a secret with the same name, even if it's the exact same spec, fails due to a name conflict ----
	_, err = ts.Client.CreateSecret(context.Background(), &validSpecRequest)
	assert.Error(t, err)
	assert.Equal(t, codes.AlreadyExists, grpc.Code(err), grpc.ErrorDesc(err))
}