cloudfoundry/korifi

View on GitHub
api/repositories/image_repository.go

Summary

Maintainability
A
35 mins
Test Coverage
B
87%
package repositories
 
import (
"context"
"errors"
"fmt"
"io"
 
"code.cloudfoundry.org/korifi/api/authorization"
apierrors "code.cloudfoundry.org/korifi/api/errors"
"code.cloudfoundry.org/korifi/tools/image"
"github.com/google/go-containerregistry/pkg/name"
 
authv1 "k8s.io/api/authorization/v1"
)
 
//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get,namespace=ROOT_NAMESPACE
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get,namespace=ROOT_NAMESPACE
 
const SourceImageResourceType = "SourceImage"
 
//counterfeiter:generate -o fake -fake-name ImagePusher . ImagePusher
 
type ImagePusher interface {
Push(ctx context.Context, creds image.Creds, repoRef string, zipReader io.Reader, tags ...string) (string, error)
}
 
type ImageRepository struct {
userClientFactory authorization.UserClientFactory
pusher ImagePusher
pushSecretNames []string
pushSecretNamespace string
}
 
func NewImageRepository(
userClientFactory authorization.UserClientFactory,
pusher ImagePusher,
pushSecretNames []string,
pushSecretNamespace string,
) *ImageRepository {
return &ImageRepository{
userClientFactory: userClientFactory,
pusher: pusher,
pushSecretNames: pushSecretNames,
pushSecretNamespace: pushSecretNamespace,
}
}
 
Method `ImageRepository.UploadSourceImage` has 6 arguments (exceeds 5 allowed). Consider refactoring.
func (r *ImageRepository) UploadSourceImage(ctx context.Context, authInfo authorization.Info, imageRef string, srcReader io.Reader, spaceGUID string, tags ...string) (string, error) {
authorized, err := r.canIPatchCFPackage(ctx, authInfo, spaceGUID)
if err != nil {
return "", fmt.Errorf("checking auth to upload source image for failed: %w", err)
}
 
if !authorized {
return "", apierrors.NewForbiddenError(errors.New("not authorized to patch cfpackage"), PackageResourceType)
}
 
_, err = name.ParseReference(imageRef)
if err != nil {
return "", apierrors.NewUnprocessableEntityError(err, fmt.Sprintf("invalid image ref: %q", imageRef))
}
 
pushedRef, err := r.pusher.Push(ctx, image.Creds{
Namespace: r.pushSecretNamespace,
SecretNames: r.pushSecretNames,
}, imageRef, srcReader, tags...)
if err != nil {
return "", apierrors.NewBlobstoreUnavailableError(fmt.Errorf("pushing image ref '%s' failed: %w", imageRef, err))
}
 
return pushedRef, nil
}
 
func (r *ImageRepository) canIPatchCFPackage(ctx context.Context, authInfo authorization.Info, spaceGUID string) (bool, error) {
userClient, err := r.userClientFactory.BuildClient(authInfo)
if err != nil {
return false, fmt.Errorf("canIPatchCFPackage: failed to create user k8s client: %w", err)
}
 
review := authv1.SelfSubjectAccessReview{
Spec: authv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authv1.ResourceAttributes{
Namespace: spaceGUID,
Verb: "patch",
Group: "korifi.cloudfoundry.org",
Resource: "cfpackages",
},
},
}
if err := userClient.Create(ctx, &review); err != nil {
return false, fmt.Errorf("canIPatchCFPackage: failed to create self subject access review: %w", apierrors.FromK8sError(err, PackageResourceType))
}
 
return review.Status.Allowed, nil
}