server/pkg/publisher/repository.go

Summary

Maintainability
A
35 mins
Test Coverage
F
0%
package publisher

import (
    "context"
    "encoding/json"
    "fmt"
    "io"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/hashicorp/go-hclog"
    "github.com/theupdateframework/go-tuf"

    "github.com/werf/trdl/server/pkg/util"
)

type S3Options struct {
    AwsConfig  *aws.Config
    BucketName string
}

type TufRepoOptions struct {
    PrivKeys TufRepoPrivKeys
}

func NewRepositoryWithOptions(s3Options S3Options, tufRepoOptions TufRepoOptions, logger hclog.Logger) (*S3Repository, error) {
    s3fs := NewS3Filesystem(s3Options.AwsConfig, s3Options.BucketName, logger)
    tufStore := NewNonAtomicTufStore(tufRepoOptions.PrivKeys, s3fs, logger)

    tufRepo, err := tuf.NewRepo(tufStore)
    if err != nil {
        return nil, fmt.Errorf("error initializing tuf repo: %w", err)
    }

    repository := NewRepository(s3fs, tufStore, tufRepo, logger)

    if err := tufStore.PrivKeys.SetupStoreSigners(tufStore); err != nil {
        return nil, fmt.Errorf("unable to set private keys into tuf store: %w", err)
    }

    return repository, nil
}

type S3Repository struct {
    S3Filesystem *S3Filesystem
    TufStore     *NonAtomicTufStore
    TufRepo      *tuf.Repo

    logger hclog.Logger
}

func NewRepository(s3Filesystem *S3Filesystem, tufStore *NonAtomicTufStore, tufRepo *tuf.Repo, logger hclog.Logger) *S3Repository {
    return &S3Repository{
        S3Filesystem: s3Filesystem,
        TufStore:     tufStore,
        TufRepo:      tufRepo,
        logger:       logger,
    }
}

func (repository *S3Repository) SetPrivKeys(privKeys TufRepoPrivKeys) error {
    repository.logger.Debug("-- S3Repository.SetPrivKeys")

    repository.TufStore.PrivKeys = privKeys

    repository.logger.Debug(fmt.Sprintf("-- S3Repository.SetPrivKeys BEFORE AddPrivateKeyWithExpires: %#v\n", privKeys))

    if err := privKeys.SetupStoreSigners(repository.TufStore); err != nil {
        return fmt.Errorf("unable to set private keys into tuf store: %w", err)
    }

    if err := privKeys.SetupTufRepoSigners(repository.TufRepo); err != nil {
        return fmt.Errorf("unable to set private keys into tuf repo: %w", err)
    }

    repository.logger.Debug(fmt.Sprintf("-- S3Repository.SetPrivKeys AFTER AddPrivateKeyWithExpires: %#v\n", privKeys))

    return nil
}

func (repository *S3Repository) GetPrivKeys() TufRepoPrivKeys {
    return repository.TufStore.PrivKeys
}

func (repository *S3Repository) GenPrivKeys() error {
    if _, err := repository.TufRepo.GenKey("root"); err != nil {
        return fmt.Errorf("error generating tuf repository root key: %w", err)
    }

    if _, err := repository.TufRepo.GenKey("targets"); err != nil {
        return fmt.Errorf("error generating tuf repository targets key: %w", err)
    }

    if _, err := repository.TufRepo.GenKey("snapshot"); err != nil {
        return fmt.Errorf("error generating tuf repository snapshot key: %w", err)
    }

    if _, err := repository.TufRepo.GenKey("timestamp"); err != nil {
        return fmt.Errorf("error generating tuf repository timestamp key: %w", err)
    }

    return nil
}

func (repository *S3Repository) RotatePrivKeys(ctx context.Context) (bool, TufRepoPrivKeys, error) {
    // TODO: Check priv keys expiration and generate new keys when necessary.

    return false, TufRepoPrivKeys{}, nil
}

func (repository *S3Repository) Init() error {
    err := repository.TufRepo.Init(false)

    if err == tuf.ErrInitNotAllowed {
        repository.logger.Info("Tuf repository already initialized: skip initialization")
    } else if err != nil {
        return fmt.Errorf("unable to init tuf repository: %w", err)
    }

    return nil
}

func (repository *S3Repository) StageTarget(ctx context.Context, pathInsideTargets string, data io.Reader) error {
    if err := repository.TufStore.StageTargetFile(ctx, pathInsideTargets, data); err != nil {
        return fmt.Errorf("unable to add staged file %q: %w", pathInsideTargets, err)
    }

    if err := repository.TufRepo.AddTarget(pathInsideTargets, json.RawMessage("")); err != nil {
        return fmt.Errorf("unable to register target file %q in the tuf repo: %w", pathInsideTargets, err)
    }

    return nil
}

func (repository *S3Repository) UpdateTimestamps(_ context.Context, systemClock util.Clock) error {
    return NewTufRepoRotator(repository.TufRepo).Rotate(repository.logger, systemClock.Now())
}

func (repository *S3Repository) CommitStaged(_ context.Context) error {
    if err := repository.TufRepo.Snapshot(); err != nil {
        return fmt.Errorf("tuf repo snapshot failed: %w", err)
    }
    if err := repository.TufRepo.Timestamp(); err != nil {
        return fmt.Errorf("tuf repo timestamp failed: %w", err)
    }
    if err := repository.TufRepo.Commit(); err != nil {
        return fmt.Errorf("unable to commit staged changes into the repo: %w", err)
    }
    return nil
}

func (repository *S3Repository) GetTargets(ctx context.Context) ([]string, error) {
    targetsMeta, err := repository.TufRepo.Targets()
    if err != nil {
        return nil, fmt.Errorf("unable to get TUF-repo targets metadata: %w", err)
    }

    var res []string
    for path := range targetsMeta {
        res = append(res, path)
    }
    return res, nil
}