Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

oci object storage target #3675

Merged
merged 5 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ cross-distro.sh:
- vsphere
- edge-commit generic.s3
- edge-container
- oci

API:
stage: test
Expand Down
7 changes: 6 additions & 1 deletion cmd/osbuild-upload-oci/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,16 @@ var uploadCmd = &cobra.Command{
}
defer file.Close()

imageID, err := uploader.Upload(objectName, bucketName, bucketNamespace, file, compartment, fileName)
err = uploader.Upload(objectName, bucketName, bucketNamespace, file)
if err != nil {
return fmt.Errorf("failed to upload the image: %v", err)
}

imageID, err := uploader.CreateImage(objectName, bucketName, bucketNamespace, compartment, fileName)
if err != nil {
return fmt.Errorf("failed to create the image from storage object: %v", err)
}

fmt.Printf("Image %s was uploaded and created successfully\n", imageID)
return nil
},
Expand Down
5 changes: 5 additions & 0 deletions cmd/osbuild-worker/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ type awsConfig struct {
Bucket string `toml:"bucket"`
}

type ociConfig struct {
Credentials string `toml:"credentials"`
}

type genericS3Config struct {
Credentials string `toml:"credentials"`
Endpoint string `toml:"endpoint"`
Expand Down Expand Up @@ -71,6 +75,7 @@ type workerConfig struct {
GenericS3 *genericS3Config `toml:"generic_s3"`
Authentication *authenticationConfig `toml:"authentication"`
Containers *containersConfig `toml:"containers"`
OCI *ociConfig `toml:"oci"`
// default value: /api/worker/v1
BasePath string `toml:"base_path"`
DNFJson string `toml:"dnf-json"`
Expand Down
6 changes: 6 additions & 0 deletions cmd/osbuild-worker/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ upload_threads = 8
credentials = "/etc/osbuild-worker/aws-creds"
bucket = "buckethead"

[oci]
credentials = "/etc/osbuild-worker/oci-creds"

[generic_s3]
credentials = "/etc/osbuild-worker/s3-creds"
endpoint = "http://s3.example.com"
Expand Down Expand Up @@ -96,6 +99,9 @@ offline_token = "/etc/osbuild-worker/offline_token"
Credentials: "/etc/osbuild-worker/aws-creds",
Bucket: "buckethead",
},
OCI: &ociConfig{
Credentials: "/etc/osbuild-worker/oci-creds",
},
GenericS3: &genericS3Config{
Credentials: "/etc/osbuild-worker/s3-creds",
Endpoint: "http://s3.example.com",
Expand Down
115 changes: 109 additions & 6 deletions cmd/osbuild-worker/jobimpl-osbuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,20 @@ type AzureConfiguration struct {
UploadThreads int
}

type OCIConfiguration struct {
ClientParams *oci.ClientParams
Compartment string
Bucket string
Namespace string
}

type OSBuildJobImpl struct {
Store string
Output string
KojiServers map[string]kojiServer
GCPConfig GCPConfiguration
AzureConfig AzureConfiguration
OCIConfig OCIConfiguration
AWSCreds string
AWSBucket string
S3Config S3Configuration
Expand Down Expand Up @@ -179,6 +187,30 @@ func (impl *OSBuildJobImpl) getGCP(credentials []byte) (*gcp.GCP, error) {
}
}

// Takes the worker config as a base and overwrites it with both t1 and t2's options
func (impl *OSBuildJobImpl) getOCI(tcp oci.ClientParams) (oci.Client, error) {
var cp oci.ClientParams
if impl.OCIConfig.ClientParams != nil {
cp = *impl.OCIConfig.ClientParams
}
if tcp.User != "" {
cp.User = tcp.User
}
if tcp.Region != "" {
cp.Region = tcp.Region
}
if tcp.Tenancy != "" {
cp.Tenancy = tcp.Tenancy
}
if tcp.PrivateKey != "" {
cp.PrivateKey = tcp.PrivateKey
}
if tcp.Fingerprint != "" {
cp.Fingerprint = tcp.Fingerprint
}
return oci.NewClient(&cp)
}

func validateResult(result *worker.OSBuildJobResult, jobID string) {
logWithId := logrus.WithField("jobId", jobID)
if result.JobError != nil {
Expand Down Expand Up @@ -848,7 +880,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
targetResult = target.NewOCITargetResult(nil)
// create an ociClient uploader with a valid storage client
var ociClient oci.Client
ociClient, err = oci.NewClient(&oci.ClientParams{
ociClient, err = impl.getOCI(oci.ClientParams{
User: targetOptions.User,
Region: targetOptions.Region,
Tenancy: targetOptions.Tenancy,
Expand All @@ -868,21 +900,92 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
}
defer file.Close()
i, _ := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
imageID, err := ociClient.Upload(
bucket := impl.OCIConfig.Bucket
if targetOptions.Bucket != "" {
bucket = targetOptions.Bucket
}
namespace := impl.OCIConfig.Namespace
if targetOptions.Namespace != "" {
namespace = targetOptions.Namespace
}
err = ociClient.Upload(
fmt.Sprintf("osbuild-upload-%d", i),
targetOptions.Bucket,
targetOptions.Namespace,
bucket,
namespace,
file,
targetOptions.Compartment,
)
if err != nil {
targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
break
}

compartment := impl.OCIConfig.Compartment
if targetOptions.Compartment != "" {
compartment = targetOptions.Compartment
}
imageID, err := ociClient.CreateImage(
fmt.Sprintf("osbuild-upload-%d", i),
bucket,
namespace,
compartment,
jobTarget.ImageName,
)
if err != nil {
targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
break
}

logWithId.Info("[OCI] 🎉 Image uploaded and registered!")
targetResult.Options = &target.OCITargetResultOptions{ImageID: imageID}
case *target.OCIObjectStorageTargetOptions:
targetResult = target.NewOCIObjectStorageTargetResult(nil)
// create an ociClient uploader with a valid storage client
ociClient, err := impl.getOCI(oci.ClientParams{
User: targetOptions.User,
Region: targetOptions.Region,
Tenancy: targetOptions.Tenancy,
Fingerprint: targetOptions.Fingerprint,
PrivateKey: targetOptions.PrivateKey,
})
if err != nil {
targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
break
}
logWithId.Info("[OCI] 🔑 Logged in OCI")
logWithId.Info("[OCI] ⬆ Uploading the image")
file, err := os.Open(path.Join(outputDirectory, jobTarget.OsbuildArtifact.ExportName, jobTarget.OsbuildArtifact.ExportFilename))
if err != nil {
targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
break
}
defer file.Close()
i, _ := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
bucket := impl.OCIConfig.Bucket
if targetOptions.Bucket != "" {
bucket = targetOptions.Bucket
}
namespace := impl.OCIConfig.Namespace
if targetOptions.Namespace != "" {
namespace = targetOptions.Namespace
}
err = ociClient.Upload(
fmt.Sprintf("osbuild-upload-%d", i),
bucket,
namespace,
file,
)
if err != nil {
targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
break
}

uri, err := ociClient.PreAuthenticatedRequest(fmt.Sprintf("osbuild-upload-%d", i), bucket, namespace)
if err != nil {
targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorGeneratingSignedURL, err.Error(), nil)
break
}
logWithId.Info("[OCI] 🎉 Image uploaded and pre-authenticated request generated!")
targetResult.Options = &target.OCIObjectStorageTargetResultOptions{URL: uri}
case *target.ContainerTargetOptions:
targetResult = target.NewContainerTargetResult(nil)
destination := jobTarget.ImageName
Expand Down
32 changes: 32 additions & 0 deletions cmd/osbuild-worker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/upload/azure"
"github.com/osbuild/osbuild-composer/internal/upload/koji"
"github.com/osbuild/osbuild-composer/internal/upload/oci"
"github.com/osbuild/osbuild-composer/internal/worker"
)

Expand Down Expand Up @@ -395,6 +396,36 @@ func main() {
containersTLSVerify = config.Containers.TLSVerify
}

var ociConfig OCIConfiguration
if config.OCI != nil {
var creds struct {
User string `toml:"user"`
Tenancy string `toml:"tenancy"`
Region string `toml:"region"`
Fingerprint string `toml:"fingerprint"`
PrivateKey string `toml:"private_key"`
Bucket string `toml:"bucket"`
Namespace string `toml:"namespace"`
Compartment string `toml:"compartment"`
}
_, err := toml.DecodeFile(config.OCI.Credentials, &creds)
if err != nil {
logrus.Fatalf("cannot load oci credentials: %v", err)
}
ociConfig = OCIConfiguration{
ClientParams: &oci.ClientParams{
User: creds.User,
Region: creds.Region,
Tenancy: creds.Tenancy,
PrivateKey: creds.PrivateKey,
Fingerprint: creds.Fingerprint,
},
Bucket: creds.Bucket,
Namespace: creds.Namespace,
Compartment: creds.Compartment,
}
}

// depsolve jobs can be done during other jobs
depsolveCtx, depsolveCtxCancel := context.WithCancel(context.Background())
solver := dnfjson.NewBaseSolver(rpmmd_cache)
Expand Down Expand Up @@ -438,6 +469,7 @@ func main() {
KojiServers: kojiServers,
GCPConfig: gcpConfig,
AzureConfig: azureConfig,
OCIConfig: ociConfig,
AWSCreds: awsCredentials,
AWSBucket: awsBucket,
S3Config: S3Configuration{
Expand Down
8 changes: 8 additions & 0 deletions internal/cloudapi/v2/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ func imageTypeFromApiImageType(it ImageTypes, arch distro.Arch) string {
return "iot-raw-image"
case ImageTypesLiveInstaller:
return "live-installer"
case ImageTypesOci:
return "oci"
case ImageTypesWsl:
return "wsl"
}
Expand Down Expand Up @@ -376,6 +378,12 @@ func targetResultToUploadStatus(t *target.TargetResult) (*UploadStatus, error) {
Url: containerOptions.URL,
Digest: containerOptions.Digest,
}
case target.TargetNameOCIObjectStorage:
uploadType = UploadTypesOciObjectstorage
ociOptions := t.Options.(*target.OCIObjectStorageTargetResultOptions)
uploadOptions = OCIUploadStatus{
Url: ociOptions.URL,
}

default:
return nil, fmt.Errorf("unknown upload target: %s", t.Name)
Expand Down
16 changes: 16 additions & 0 deletions internal/cloudapi/v2/imagerequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,22 @@ func (ir *ImageRequest) GetTarget(request *ComposeRequest, imageType distro.Imag
}
t.OsbuildArtifact.ExportFilename = imageType.Filename()

irTarget = t
case ImageTypesOci:
var ociUploadOptions OCIUploadOptions
jsonUploadOptions, err := json.Marshal(*ir.UploadOptions)
if err != nil {
return nil, HTTPError(ErrorJSONMarshallingError)
}
err = json.Unmarshal(jsonUploadOptions, &ociUploadOptions)
if err != nil {
return nil, HTTPError(ErrorJSONUnMarshallingError)
}

key := fmt.Sprintf("composer-api-%s", uuid.New().String())
t := target.NewOCIObjectStorageTarget(&target.OCIObjectStorageTargetOptions{})
t.ImageName = key
t.OsbuildArtifact.ExportFilename = imageType.Filename()
irTarget = t
default:
return nil, HTTPError(ErrorUnsupportedImageType)
Expand Down
Loading
Loading