diff --git a/cmd/image-builder-db-test/main_test.go b/cmd/image-builder-db-test/main_test.go index 8cb292032..816c15555 100644 --- a/cmd/image-builder-db-test/main_test.go +++ b/cmd/image-builder-db-test/main_test.go @@ -227,79 +227,6 @@ func testDeleteCompose(ctx context.Context, t *testing.T) { require.Equal(t, 1, count) } -func testClones(ctx context.Context, t *testing.T) { - d, err := db.InitDBConnectionPool(ctx, tutils.ConnStr(t)) - require.NoError(t, err) - conn := tutils.Connect(t) - defer conn.Close(ctx) - - composeId := uuid.New() - cloneId := uuid.New() - cloneId2 := uuid.New() - - // fkey constraint on compose id - require.Error(t, d.InsertClone(ctx, composeId, cloneId, []byte(` -{ - "region": "us-east-2" -} -`))) - - require.NoError(t, d.InsertCompose(ctx, composeId, ANR1, EMAIL1, ORGID1, nil, []byte(` -{ - "customizations": { - }, - "distribution": "rhel-8", - "image_requests": [ - { - "architecture": "x86_64", - "image_type": "guest-image", - "upload_request": { - "type": "aws.s3", - "options": { - } - } - } - ] -}`), nil, nil)) - - require.NoError(t, d.InsertClone(ctx, composeId, cloneId, []byte(` -{ - "region": "us-east-2" -} -`))) - require.NoError(t, d.InsertClone(ctx, composeId, cloneId2, []byte(` -{ - "region": "eu-central-1" -} -`))) - - clones, count, err := d.GetClonesForCompose(ctx, composeId, ORGID2, 100, 0) - require.NoError(t, err) - require.Empty(t, clones) - require.Equal(t, 0, count) - - clones, count, err = d.GetClonesForCompose(ctx, composeId, ORGID1, 1, 0) - require.NoError(t, err) - require.Len(t, clones, 1) - require.Equal(t, 2, count) - require.Equal(t, cloneId2, clones[0].Id) - - clones, count, err = d.GetClonesForCompose(ctx, composeId, ORGID1, 100, 0) - require.NoError(t, err) - require.Len(t, clones, 2) - require.Equal(t, 2, count) - require.Equal(t, cloneId2, clones[0].Id) - require.Equal(t, cloneId, clones[1].Id) - - entry, err := d.GetClone(ctx, cloneId, ORGID2) - require.ErrorIs(t, err, db.ErrCloneNotFound) - require.Nil(t, entry) - - entry, err = d.GetClone(ctx, cloneId, ORGID1) - require.NoError(t, err) - require.Equal(t, clones[1], *entry) -} - func testBlueprints(ctx context.Context, t *testing.T) { d, err := db.InitDBConnectionPool(ctx, tutils.ConnStr(t)) require.NoError(t, err) @@ -692,7 +619,6 @@ func TestAll(t *testing.T) { testCountComposesSince, testGetComposeImageType, testDeleteCompose, - testClones, testBlueprints, testGetBlueprintComposes, } diff --git a/cmd/image-builder-maintenance/db.go b/cmd/image-builder-maintenance/db.go index 070883ea2..0beb6493f 100644 --- a/cmd/image-builder-maintenance/db.go +++ b/cmd/image-builder-maintenance/db.go @@ -13,13 +13,6 @@ const ( sqlDeleteComposes = ` DELETE FROM composes WHERE created_at < $1` - sqlExpiredClonesCount = ` - SELECT COUNT(*) FROM clones - WHERE compose_id in ( - SELECT job_id - FROM composes - WHERE created_at < $1 - )` sqlExpiredComposesCount = ` SELECT COUNT(*) FROM composes WHERE created_at < $1` @@ -60,15 +53,6 @@ func (d *maintenanceDB) DeleteComposes(ctx context.Context, emailRetentionDate t return tag.RowsAffected(), nil } -func (d *maintenanceDB) ExpiredClonesCount(ctx context.Context, emailRetentionDate time.Time) (int64, error) { - var count int64 - err := d.Conn.QueryRow(ctx, sqlExpiredClonesCount, emailRetentionDate).Scan(&count) - if err != nil { - return 0, err - } - return count, nil -} - func (d *maintenanceDB) ExpiredComposesCount(ctx context.Context, emailRetentionDate time.Time) (int64, error) { var count int64 err := d.Conn.QueryRow(ctx, sqlExpiredComposesCount, emailRetentionDate).Scan(&count) @@ -159,7 +143,6 @@ func DBCleanup(ctx context.Context, dbURL string, dryRun bool, ComposesRetention slog.ErrorContext(ctx, "error running vacuum stats", "err", err) } - var rowsClones int64 var rows int64 emailRetentionDate := time.Now().AddDate(0, ComposesRetentionMonths*-1, 0) @@ -175,16 +158,11 @@ func DBCleanup(ctx context.Context, dbURL string, dryRun bool, ComposesRetention // so `break` works as expected } if dryRun { - rowsClones, err = db.ExpiredClonesCount(ctx, emailRetentionDate) - if err != nil { - slog.ErrorContext(ctx, "error querying expired clones", "err", err) - } - rows, err = db.ExpiredComposesCount(ctx, emailRetentionDate) if err != nil { slog.WarnContext(ctx, "error querying expired composes", "err", err) } - slog.InfoContext(ctx, "dryrun", "expired_composes_count", rows, "expired_clones_count", rowsClones) + slog.InfoContext(ctx, "dryrun", "expired_composes_count", rows) break } diff --git a/cmd/image-builder-maintenance/db_test.go b/cmd/image-builder-maintenance/db_test.go index adf2fc5e3..73ea0e0db 100644 --- a/cmd/image-builder-maintenance/db_test.go +++ b/cmd/image-builder-maintenance/db_test.go @@ -8,7 +8,6 @@ import ( "time" "github.com/google/uuid" - "github.com/osbuild/image-builder-crc/internal/db" "github.com/osbuild/image-builder-crc/internal/tutils" "github.com/stretchr/testify/require" ) @@ -80,9 +79,6 @@ func testExpireByCallingDBCleanup(ctx context.Context, t *testing.T) { d, err := newDB(ctx, connStr) require.NoError(t, err) - internalDB, err := db.InitDBConnectionPool(ctx, connStr) - require.NoError(t, err) - dbComposesRetentionMonths := 5 notYetExpiredTime := time.Now() @@ -97,32 +93,17 @@ func testExpireByCallingDBCleanup(ctx context.Context, t *testing.T) { insert = "INSERT INTO composes(job_id, request, created_at, account_number, org_id) VALUES ($1, $2, $3, $4, $5)" _, err = d.Conn.Exec(ctx, insert, composeIdExpired, "{}", alreadyExpiredTime, ANR1, ORGID1) - cloneId := uuid.New() - require.NoError(t, internalDB.InsertClone(ctx, composeIdExpired, cloneId, []byte(` -{ - "region": "us-east-2" -} -`))) - // two rows inserted, only one is expired rows, err := d.ExpiredComposesCount(ctx, emailRetentionDate) require.NoError(t, err) require.Equal(t, int64(1), rows) - rows, err = d.ExpiredClonesCount(ctx, emailRetentionDate) - require.NoError(t, err) - require.Equal(t, int64(1), rows) - err = DBCleanup(ctx, connStr, false, dbComposesRetentionMonths) require.NoError(t, err) rows, err = d.ExpiredComposesCount(ctx, emailRetentionDate) require.NoError(t, err) require.Equal(t, int64(0), rows) - - rows, err = d.ExpiredClonesCount(ctx, emailRetentionDate) - require.NoError(t, err) - require.Equal(t, int64(0), rows) } // testVacuum test if no vacuum is performed on a clean database diff --git a/internal/clients/composer/client.go b/internal/clients/composer/client.go index e26778e52..bc49bc872 100644 --- a/internal/clients/composer/client.go +++ b/internal/clients/composer/client.go @@ -140,15 +140,3 @@ func (cc *ComposerClient) Compose(ctx context.Context, compose ComposeRequest) ( func (cc *ComposerClient) OpenAPI(ctx context.Context) (*http.Response, error) { return cc.request(ctx, "GET", fmt.Sprintf("%s/openapi", cc.composerURL), nil, nil) } - -func (cc *ComposerClient) CloneCompose(ctx context.Context, id uuid.UUID, clone CloneComposeBody) (*http.Response, error) { - buf, err := json.Marshal(clone) - if err != nil { - return nil, err - } - return cc.request(ctx, "POST", fmt.Sprintf("%s/composes/%s/clone", cc.composerURL, id), contentHeaders, bytes.NewReader(buf)) -} - -func (cc *ComposerClient) CloneStatus(ctx context.Context, id uuid.UUID) (*http.Response, error) { - return cc.request(ctx, "GET", fmt.Sprintf("%s/clones/%s", cc.composerURL, id), nil, nil) -} diff --git a/internal/db/db.go b/internal/db/db.go index 4620acfc1..a835ec438 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -15,7 +15,6 @@ import ( // ErrComposeEntryNotFound occurs when no compose request is found for a user. var ErrComposeEntryNotFound = errors.New("compose entry not found") -var ErrCloneNotFound = errors.New("clone not found") var ErrBlueprintNotFound = errors.New("blueprint not found") var ErrAffectedRowsMismatch = errors.New("unexpected affected rows") @@ -37,13 +36,6 @@ type ComposeWithBlueprintVersion struct { BlueprintVersion *int } -type CloneEntry struct { - Id uuid.UUID - ComposeId uuid.UUID - Request json.RawMessage - CreatedAt time.Time -} - type BlueprintEntry struct { Id uuid.UUID VersionId uuid.UUID @@ -83,10 +75,6 @@ type DB interface { CountBlueprintComposesSince(ctx context.Context, orgId string, blueprintId uuid.UUID, blueprintVersion *int, since time.Duration, ignoreImageTypes []string) (int, error) DeleteCompose(ctx context.Context, jobId uuid.UUID, orgId string) error - InsertClone(ctx context.Context, composeId, cloneId uuid.UUID, request json.RawMessage) error - GetClonesForCompose(ctx context.Context, composeId uuid.UUID, orgId string, limit, offset int) ([]CloneEntry, int, error) - GetClone(ctx context.Context, id uuid.UUID, orgId string) (*CloneEntry, error) - InsertBlueprint(ctx context.Context, id uuid.UUID, versionId uuid.UUID, orgID, accountNumber, name, description string, body json.RawMessage, metadata json.RawMessage, serviceSnapshots json.RawMessage) error GetBlueprint(ctx context.Context, id uuid.UUID, orgID string, version *int) (*BlueprintEntry, error) UpdateBlueprint(ctx context.Context, id uuid.UUID, blueprintId uuid.UUID, orgId string, name string, description string, body json.RawMessage, serviceSnapshots json.RawMessage) error @@ -137,36 +125,6 @@ const ( SET deleted = TRUE WHERE org_id=$1 AND job_id=$2 ` - - sqlInsertClone = ` - INSERT INTO clones(id, compose_id, request, created_at) - VALUES($1, $2, $3, CURRENT_TIMESTAMP)` - - sqlGetClonesForCompose = ` - SELECT clones.id, clones.compose_id, clones.request, clones.created_at - FROM clones - WHERE clones.compose_id=$1 AND $1 in ( - SELECT composes.job_id - FROM composes - WHERE composes.org_id=$2) - ORDER BY created_at DESC - LIMIT $3 OFFSET $4` - - sqlCountClonesForCompose = ` - SELECT COUNT(*) - FROM clones - WHERE clones.compose_id=$1 AND $1 in ( - SELECT composes.job_id - FROM composes - WHERE composes.org_id=$2)` - - sqlGetClone = ` - SELECT clones.id, clones.compose_id, clones.request, clones.created_at - FROM clones - WHERE clones.id=$1 AND clones.compose_id in ( - SELECT composes.job_id - FROM composes - WHERE composes.org_id=$2)` ) func InitDBConnectionPool(ctx context.Context, connStr string) (DB, error) { @@ -323,78 +281,3 @@ func (db *dB) DeleteCompose(ctx context.Context, jobId uuid.UUID, orgId string) return err } - -func (db *dB) InsertClone(ctx context.Context, composeId, cloneId uuid.UUID, request json.RawMessage) error { - conn, err := db.Pool.Acquire(ctx) - if err != nil { - return err - } - defer conn.Release() - - _, err = conn.Exec(ctx, sqlInsertClone, cloneId, composeId, request) - return err -} - -func (db *dB) GetClonesForCompose(ctx context.Context, composeId uuid.UUID, orgId string, limit, offset int) ([]CloneEntry, int, error) { - conn, err := db.Pool.Acquire(ctx) - if err != nil { - return nil, 0, err - } - defer conn.Release() - - rows, err := conn.Query(ctx, sqlGetClonesForCompose, composeId, orgId, limit, offset) - if err != nil { - return nil, 0, err - } - defer rows.Close() - - var clones []CloneEntry - for rows.Next() { - var id uuid.UUID - var composeID uuid.UUID - var request json.RawMessage - var createdAt time.Time - err = rows.Scan(&id, &composeID, &request, &createdAt) - if err != nil { - return nil, 0, err - } - clones = append(clones, CloneEntry{ - id, - composeID, - request, - createdAt, - }) - } - if err = rows.Err(); err != nil { - return nil, 0, err - } - - var count int - err = conn.QueryRow(ctx, sqlCountClonesForCompose, composeId, orgId).Scan(&count) - if err != nil { - return nil, 0, err - } - - return clones, count, nil - -} - -func (db *dB) GetClone(ctx context.Context, id uuid.UUID, orgId string) (*CloneEntry, error) { - conn, err := db.Pool.Acquire(ctx) - if err != nil { - return nil, err - } - defer conn.Release() - - var clone CloneEntry - err = conn.QueryRow(ctx, sqlGetClone, id, orgId).Scan(&clone.Id, &clone.ComposeId, &clone.Request, &clone.CreatedAt) - if err != nil { - if errors.Is(err, pgx.ErrNoRows) { - return nil, ErrCloneNotFound - } else { - return nil, err - } - } - - return &clone, nil -} diff --git a/internal/db/migrations-tern/015_drop_table_clones.sql b/internal/db/migrations-tern/015_drop_table_clones.sql new file mode 100644 index 000000000..7b82b57fb --- /dev/null +++ b/internal/db/migrations-tern/015_drop_table_clones.sql @@ -0,0 +1,2 @@ +-- Drop the clones table as cloning support is being removed +DROP TABLE IF EXISTS clones; diff --git a/internal/v1/api.go b/internal/v1/api.go index 3efce721c..e24e7ff50 100644 --- a/internal/v1/api.go +++ b/internal/v1/api.go @@ -27,14 +27,6 @@ const ( Ui ClientId = "ui" ) -// Defines values for CloneStatusResponseStatus. -const ( - CloneStatusResponseStatusFailure CloneStatusResponseStatus = "failure" - CloneStatusResponseStatusPending CloneStatusResponseStatus = "pending" - CloneStatusResponseStatusRunning CloneStatusResponseStatus = "running" - CloneStatusResponseStatusSuccess CloneStatusResponseStatus = "success" -) - // Defines values for CustomizationsPartitioningMode. const ( AutoLvm CustomizationsPartitioningMode = "auto-lvm" @@ -158,10 +150,10 @@ const ( // Defines values for UploadStatusStatus. const ( - Failure UploadStatusStatus = "failure" - Pending UploadStatusStatus = "pending" - Running UploadStatusStatus = "running" - Success UploadStatusStatus = "success" + UploadStatusStatusFailure UploadStatusStatus = "failure" + UploadStatusStatusPending UploadStatusStatus = "pending" + UploadStatusStatusRunning UploadStatusStatus = "running" + UploadStatusStatusSuccess UploadStatusStatus = "success" ) // Defines values for UploadTypes. @@ -189,18 +181,6 @@ type AAPRegistration struct { TlsCertificateAuthority string `json:"tls_certificate_authority,omitempty"` } -// AWSEC2Clone defines model for AWSEC2Clone. -type AWSEC2Clone struct { - // Region A region as described in - // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-regions - Region string `json:"region"` - - // ShareWithAccounts An array of AWS account IDs as described in - // https://docs.aws.amazon.com/IAM/latest/UserGuide/console_account-alias.html - ShareWithAccounts *[]string `json:"share_with_accounts,omitempty"` - ShareWithSources *[]string `json:"share_with_sources,omitempty"` -} - // AWSS3UploadRequestOptions defines model for AWSS3UploadRequestOptions. type AWSS3UploadRequestOptions = map[string]interface{} @@ -357,48 +337,6 @@ type CACertsCustomization struct { // ClientId defines model for ClientId. type ClientId string -// CloneRequest defines model for CloneRequest. -type CloneRequest struct { - union json.RawMessage -} - -// CloneResponse defines model for CloneResponse. -type CloneResponse struct { - Id openapi_types.UUID `json:"id"` -} - -// CloneStatusResponse defines model for CloneStatusResponse. -type CloneStatusResponse struct { - ComposeId *openapi_types.UUID `json:"compose_id,omitempty"` - Options CloneStatusResponse_Options `json:"options"` - Status CloneStatusResponseStatus `json:"status"` - Type UploadTypes `json:"type"` -} - -// CloneStatusResponse_Options defines model for CloneStatusResponse.Options. -type CloneStatusResponse_Options struct { - union json.RawMessage -} - -// CloneStatusResponseStatus defines model for CloneStatusResponse.Status. -type CloneStatusResponseStatus string - -// ClonesResponse defines model for ClonesResponse. -type ClonesResponse struct { - Data []ClonesResponseItem `json:"data"` - Links ListResponseLinks `json:"links"` - Meta ListResponseMeta `json:"meta"` -} - -// ClonesResponseItem defines model for ClonesResponseItem. -type ClonesResponseItem struct { - // ComposeId UUID of the parent compose of the clone - ComposeId openapi_types.UUID `json:"compose_id"` - CreatedAt string `json:"created_at"` - Id openapi_types.UUID `json:"id"` - Request CloneRequest `json:"request"` -} - // ComposeMetadata defines model for ComposeMetadata. type ComposeMetadata struct { // OstreeCommit ID (hash) of the built commit @@ -1177,15 +1115,6 @@ type GetComposesParams struct { IgnoreImageTypes *[]ImageTypes `form:"ignoreImageTypes,omitempty" json:"ignoreImageTypes,omitempty"` } -// GetComposeClonesParams defines parameters for GetComposeClones. -type GetComposeClonesParams struct { - // Limit max amount of clones, default 100 - Limit *int `form:"limit,omitempty" json:"limit,omitempty"` - - // Offset clones page offset, default 0 - Offset *int `form:"offset,omitempty" json:"offset,omitempty"` -} - // GetPackagesParams defines parameters for GetPackages. type GetPackagesParams struct { // Distribution distribution to look up packages for @@ -1219,188 +1148,9 @@ type ComposeBlueprintJSONRequestBody ComposeBlueprintJSONBody // ComposeImageJSONRequestBody defines body for ComposeImage for application/json ContentType. type ComposeImageJSONRequestBody = ComposeRequest -// CloneComposeJSONRequestBody defines body for CloneCompose for application/json ContentType. -type CloneComposeJSONRequestBody = CloneRequest - // RecommendPackageJSONRequestBody defines body for RecommendPackage for application/json ContentType. type RecommendPackageJSONRequestBody = RecommendPackageRequest -// AsAWSEC2Clone returns the union data inside the CloneRequest as a AWSEC2Clone -func (t CloneRequest) AsAWSEC2Clone() (AWSEC2Clone, error) { - var body AWSEC2Clone - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromAWSEC2Clone overwrites any union data inside the CloneRequest as the provided AWSEC2Clone -func (t *CloneRequest) FromAWSEC2Clone(v AWSEC2Clone) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeAWSEC2Clone performs a merge with any union data inside the CloneRequest, using the provided AWSEC2Clone -func (t *CloneRequest) MergeAWSEC2Clone(v AWSEC2Clone) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t CloneRequest) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *CloneRequest) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsAWSUploadStatus returns the union data inside the CloneStatusResponse_Options as a AWSUploadStatus -func (t CloneStatusResponse_Options) AsAWSUploadStatus() (AWSUploadStatus, error) { - var body AWSUploadStatus - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromAWSUploadStatus overwrites any union data inside the CloneStatusResponse_Options as the provided AWSUploadStatus -func (t *CloneStatusResponse_Options) FromAWSUploadStatus(v AWSUploadStatus) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeAWSUploadStatus performs a merge with any union data inside the CloneStatusResponse_Options, using the provided AWSUploadStatus -func (t *CloneStatusResponse_Options) MergeAWSUploadStatus(v AWSUploadStatus) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsAWSS3UploadStatus returns the union data inside the CloneStatusResponse_Options as a AWSS3UploadStatus -func (t CloneStatusResponse_Options) AsAWSS3UploadStatus() (AWSS3UploadStatus, error) { - var body AWSS3UploadStatus - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromAWSS3UploadStatus overwrites any union data inside the CloneStatusResponse_Options as the provided AWSS3UploadStatus -func (t *CloneStatusResponse_Options) FromAWSS3UploadStatus(v AWSS3UploadStatus) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeAWSS3UploadStatus performs a merge with any union data inside the CloneStatusResponse_Options, using the provided AWSS3UploadStatus -func (t *CloneStatusResponse_Options) MergeAWSS3UploadStatus(v AWSS3UploadStatus) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsGCPUploadStatus returns the union data inside the CloneStatusResponse_Options as a GCPUploadStatus -func (t CloneStatusResponse_Options) AsGCPUploadStatus() (GCPUploadStatus, error) { - var body GCPUploadStatus - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromGCPUploadStatus overwrites any union data inside the CloneStatusResponse_Options as the provided GCPUploadStatus -func (t *CloneStatusResponse_Options) FromGCPUploadStatus(v GCPUploadStatus) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeGCPUploadStatus performs a merge with any union data inside the CloneStatusResponse_Options, using the provided GCPUploadStatus -func (t *CloneStatusResponse_Options) MergeGCPUploadStatus(v GCPUploadStatus) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsAzureUploadStatus returns the union data inside the CloneStatusResponse_Options as a AzureUploadStatus -func (t CloneStatusResponse_Options) AsAzureUploadStatus() (AzureUploadStatus, error) { - var body AzureUploadStatus - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromAzureUploadStatus overwrites any union data inside the CloneStatusResponse_Options as the provided AzureUploadStatus -func (t *CloneStatusResponse_Options) FromAzureUploadStatus(v AzureUploadStatus) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeAzureUploadStatus performs a merge with any union data inside the CloneStatusResponse_Options, using the provided AzureUploadStatus -func (t *CloneStatusResponse_Options) MergeAzureUploadStatus(v AzureUploadStatus) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsOCIUploadStatus returns the union data inside the CloneStatusResponse_Options as a OCIUploadStatus -func (t CloneStatusResponse_Options) AsOCIUploadStatus() (OCIUploadStatus, error) { - var body OCIUploadStatus - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromOCIUploadStatus overwrites any union data inside the CloneStatusResponse_Options as the provided OCIUploadStatus -func (t *CloneStatusResponse_Options) FromOCIUploadStatus(v OCIUploadStatus) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeOCIUploadStatus performs a merge with any union data inside the CloneStatusResponse_Options, using the provided OCIUploadStatus -func (t *CloneStatusResponse_Options) MergeOCIUploadStatus(v OCIUploadStatus) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t CloneStatusResponse_Options) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *CloneStatusResponse_Options) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - // AsDirectoryGroup0 returns the union data inside the Directory_Group as a DirectoryGroup0 func (t Directory_Group) AsDirectoryGroup0() (DirectoryGroup0, error) { var body DirectoryGroup0 @@ -2020,9 +1770,6 @@ type ServerInterface interface { // export a blueprint // (GET /blueprints/{id}/export) ExportBlueprint(ctx echo.Context, id openapi_types.UUID) error - // get status of a compose clone - // (GET /clones/{id}) - GetCloneStatus(ctx echo.Context, id openapi_types.UUID) error // compose image // (POST /compose) ComposeImage(ctx echo.Context) error @@ -2035,12 +1782,6 @@ type ServerInterface interface { // get status of an image compose // (GET /composes/{composeId}) GetComposeStatus(ctx echo.Context, composeId openapi_types.UUID) error - // clone a compose - // (POST /composes/{composeId}/clone) - CloneCompose(ctx echo.Context, composeId openapi_types.UUID) error - // get clones of a compose - // (GET /composes/{composeId}/clones) - GetComposeClones(ctx echo.Context, composeId openapi_types.UUID, params GetComposeClonesParams) error // get metadata of an image compose // (GET /composes/{composeId}/metadata) GetComposeMetadata(ctx echo.Context, composeId openapi_types.UUID) error @@ -2277,22 +2018,6 @@ func (w *ServerInterfaceWrapper) ExportBlueprint(ctx echo.Context) error { return err } -// GetCloneStatus converts echo context to params. -func (w *ServerInterfaceWrapper) GetCloneStatus(ctx echo.Context) error { - var err error - // ------------- Path parameter "id" ------------- - var id openapi_types.UUID - - err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetCloneStatus(ctx, id) - return err -} - // ComposeImage converts echo context to params. func (w *ServerInterfaceWrapper) ComposeImage(ctx echo.Context) error { var err error @@ -2366,54 +2091,6 @@ func (w *ServerInterfaceWrapper) GetComposeStatus(ctx echo.Context) error { return err } -// CloneCompose converts echo context to params. -func (w *ServerInterfaceWrapper) CloneCompose(ctx echo.Context) error { - var err error - // ------------- Path parameter "composeId" ------------- - var composeId openapi_types.UUID - - err = runtime.BindStyledParameterWithOptions("simple", "composeId", ctx.Param("composeId"), &composeId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter composeId: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.CloneCompose(ctx, composeId) - return err -} - -// GetComposeClones converts echo context to params. -func (w *ServerInterfaceWrapper) GetComposeClones(ctx echo.Context) error { - var err error - // ------------- Path parameter "composeId" ------------- - var composeId openapi_types.UUID - - err = runtime.BindStyledParameterWithOptions("simple", "composeId", ctx.Param("composeId"), &composeId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter composeId: %s", err)) - } - - // Parameter object where we will unmarshal all parameters from the context - var params GetComposeClonesParams - // ------------- Optional query parameter "limit" ------------- - - err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) - } - - // ------------- Optional query parameter "offset" ------------- - - err = runtime.BindQueryParameter("form", true, false, "offset", ctx.QueryParams(), ¶ms.Offset) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter offset: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetComposeClones(ctx, composeId, params) - return err -} - // GetComposeMetadata converts echo context to params. func (w *ServerInterfaceWrapper) GetComposeMetadata(ctx echo.Context) error { var err error @@ -2629,13 +2306,10 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.POST(baseURL+"/blueprints/:id/compose", wrapper.ComposeBlueprint) router.GET(baseURL+"/blueprints/:id/composes", wrapper.GetBlueprintComposes) router.GET(baseURL+"/blueprints/:id/export", wrapper.ExportBlueprint) - router.GET(baseURL+"/clones/:id", wrapper.GetCloneStatus) router.POST(baseURL+"/compose", wrapper.ComposeImage) router.GET(baseURL+"/composes", wrapper.GetComposes) router.DELETE(baseURL+"/composes/:composeId", wrapper.DeleteCompose) router.GET(baseURL+"/composes/:composeId", wrapper.GetComposeStatus) - router.POST(baseURL+"/composes/:composeId/clone", wrapper.CloneCompose) - router.GET(baseURL+"/composes/:composeId/clones", wrapper.GetComposeClones) router.GET(baseURL+"/composes/:composeId/metadata", wrapper.GetComposeMetadata) router.GET(baseURL+"/distributions", wrapper.GetDistributions) router.POST(baseURL+"/experimental/blueprints/:id/fixup", wrapper.FixupBlueprint) diff --git a/internal/v1/api.yaml b/internal/v1/api.yaml index 4c3839e41..a935840b3 100644 --- a/internal/v1/api.yaml +++ b/internal/v1/api.yaml @@ -477,99 +477,6 @@ paths: application/json: schema: $ref: '#/components/schemas/ComposeMetadata' - /composes/{composeId}/clone: - post: - summary: clone a compose - description: | - Clones a compose. Only composes with the 'aws' image type currently support cloning. - parameters: - - in: path - name: composeId - schema: - type: string - format: uuid - example: '123e4567-e89b-12d3-a456-426655440000' - required: true - description: Id of compose to clone - operationId: cloneCompose - tags: - - compose - requestBody: - required: true - description: details of the new clone - content: - application/json: - schema: - $ref: "#/components/schemas/CloneRequest" - responses: - '201': - description: cloning has started - content: - application/json: - schema: - $ref: "#/components/schemas/CloneResponse" - /composes/{composeId}/clones: - get: - summary: get clones of a compose - parameters: - - in: path - name: composeId - schema: - type: string - format: uuid - example: '123e4567-e89b-12d3-a456-426655440000' - required: true - description: Id of compose to get the clones of - - in: query - name: limit - schema: - type: integer - default: 100 - minimum: 1 - maximum: 100 - description: max amount of clones, default 100 - - in: query - name: offset - schema: - type: integer - default: 0 - minimum: 0 - description: clones page offset, default 0 - description: | - Returns a list of all the clones which were started for a compose - operationId: getComposeClones - tags: - - compose - responses: - '200': - description: compose clones - content: - application/json: - schema: - $ref: '#/components/schemas/ClonesResponse' - /clones/{id}: - get: - summary: get status of a compose clone - parameters: - - in: path - name: id - schema: - type: string - format: uuid - example: '123e4567-e89b-12d3-a456-426655440000' - required: true - description: Id of clone status to get - description: status of a clone - operationId: getCloneStatus - tags: - - compose - responses: - '200': - description: clone status - content: - application/json: - schema: - $ref: '#/components/schemas/CloneStatusResponse' /compose: post: summary: compose image @@ -918,16 +825,6 @@ components: total: type: integer description: Total amount of steps in the build. - CloneStatusResponse: - required: - - compose_id - allOf: - - type: object - properties: - compose_id: - type: string - format: uuid - - $ref: '#/components/schemas/UploadStatus' UploadStatus: required: - status @@ -1608,75 +1505,6 @@ components: type: string modelVersion: type: string - ClonesResponse: - required: - - meta - - links - - data - properties: - meta: - $ref: '#/components/schemas/ListResponseMeta' - links: - $ref: '#/components/schemas/ListResponseLinks' - data: - type: array - items: - $ref: '#/components/schemas/ClonesResponseItem' - ClonesResponseItem: - required: - - id - - compose_id - - request - - created_at - properties: - id: - type: string - format: uuid - compose_id: - type: string - format: uuid - description: 'UUID of the parent compose of the clone' - request: - $ref: '#/components/schemas/CloneRequest' - created_at: - type: string - CloneRequest: - oneOf: - - $ref: '#/components/schemas/AWSEC2Clone' - AWSEC2Clone: - type: object - required: - - region - properties: - region: - type: string - description: | - A region as described in - https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-regions - share_with_accounts: - type: array - maxItems: 100 - example: ['123456789012'] - description: | - An array of AWS account IDs as described in - https://docs.aws.amazon.com/IAM/latest/UserGuide/console_account-alias.html - items: - type: string - pattern: '^[0-9]{12}$' - share_with_sources: - type: array - example: ['12345'] - items: - type: string - uniqueItems: true - CloneResponse: - required: - - id - properties: - id: - type: string - format: uuid - example: '123e4567-e89b-12d3-a456-426655440000' DistributionProfileResponse: type: array description: | diff --git a/internal/v1/handler.go b/internal/v1/handler.go index e8eebbf0e..4c302172a 100644 --- a/internal/v1/handler.go +++ b/internal/v1/handler.go @@ -8,13 +8,11 @@ import ( "net/http" "net/url" "strconv" - "strings" "time" "github.com/google/uuid" "github.com/labstack/echo/v4" "github.com/osbuild/image-builder-crc/internal/clients/composer" - "github.com/osbuild/image-builder-crc/internal/clients/provisioning" "github.com/osbuild/image-builder-crc/internal/common" "github.com/osbuild/image-builder-crc/internal/db" "github.com/osbuild/image-builder-crc/internal/distribution" @@ -605,233 +603,6 @@ func (h *Handlers) GetComposes(ctx echo.Context, params GetComposesParams) error }) } -func (h *Handlers) CloneCompose(ctx echo.Context, composeId uuid.UUID) error { - err := h.canUserAccessComposeId(ctx, composeId) - if err != nil { - return err - } - - userID, err := h.server.getIdentity(ctx) - if err != nil { - return err - } - imageType, err := h.server.db.GetComposeImageType(ctx.Request().Context(), composeId, userID.OrgID()) - if err != nil { - if errors.Is(err, db.ErrComposeEntryNotFound) { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to find compose %v", composeId)) - } - ctx.Logger().Errorf("Error querying image type for compose %v: %v", composeId, err) - return echo.NewHTTPError(http.StatusInternalServerError, "Something went wrong querying the compose") - } - - var resp *http.Response - var rawCR json.RawMessage - if ImageTypes(imageType) == ImageTypesAws || ImageTypes(imageType) == ImageTypesAmi { - var awsEC2CloneReq AWSEC2Clone - err = ctx.Bind(&awsEC2CloneReq) - if err != nil { - return err - } - - rawCR, err = json.Marshal(awsEC2CloneReq) - if err != nil { - return err - } - - var shareWithAccounts []string - if awsEC2CloneReq.ShareWithAccounts != nil { - shareWithAccounts = append(shareWithAccounts, *awsEC2CloneReq.ShareWithAccounts...) - } - - if awsEC2CloneReq.ShareWithSources != nil { - for _, source := range *awsEC2CloneReq.ShareWithSources { - resp, err := h.server.pClient.GetUploadInfo(ctx.Request().Context(), source) - if err != nil { - ctx.Logger().Error(err) - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to request source: %s", source)) - } - defer closeBody(ctx, resp.Body) - - var uploadInfo provisioning.V1SourceUploadInfoResponse - err = json.NewDecoder(resp.Body).Decode(&uploadInfo) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Unable to resolve source: %s", source)) - } - - if uploadInfo.Aws == nil || uploadInfo.Aws.AccountId == nil || len(*uploadInfo.Aws.AccountId) != 12 { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to resolve source %s to an aws account id: %v", source, uploadInfo.Aws.AccountId)) - } - - ctx.Logger().Info(fmt.Sprintf("Resolved source %s, to account id %s", strings.Replace(source, "\n", "", -1), *uploadInfo.Aws.AccountId)) - shareWithAccounts = append(shareWithAccounts, *uploadInfo.Aws.AccountId) - } - } - - var ccb composer.CloneComposeBody - err = ccb.FromAWSEC2CloneCompose(composer.AWSEC2CloneCompose{ - Region: awsEC2CloneReq.Region, - ShareWithAccounts: &shareWithAccounts, - }) - if err != nil { - return err - } - - resp, err = h.server.cClient.CloneCompose(ctx.Request().Context(), composeId, ccb) - if err != nil { - return err - } - } else { - return echo.NewHTTPError(http.StatusBadRequest, "Cloning a compose is only available for AWS composes") - } - - if resp == nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Something went wrong creating the clone") - } - defer closeBody(ctx, resp.Body) - if resp.StatusCode != http.StatusCreated { - var cError composer.Error - err = json.NewDecoder(resp.Body).Decode(&cError) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Unable to parse error returned by image-builder-composer service") - } - if cError.Code == ComposeRunningOrFailedError { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("image-builder-composer compose failed: %s", cError.Reason)) - } - return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("image-builder-composer service returned an error: %s", cError.Reason)) - } - - var cloneResponse composer.CloneComposeResponse - err = json.NewDecoder(resp.Body).Decode(&cloneResponse) - if err != nil { - ctx.Logger().Errorf("unable to decode CloneComposeResponse: %v", err) - return err - } - - err = h.server.db.InsertClone(ctx.Request().Context(), composeId, cloneResponse.Id, rawCR) - if err != nil { - ctx.Logger().Errorf("Error inserting clone into db for compose %v: %v", err, composeId) - return echo.NewHTTPError(http.StatusInternalServerError, "Something went wrong saving the clone") - } - - return ctx.JSON(http.StatusCreated, CloneResponse{ - Id: cloneResponse.Id, - }) -} - -func (h *Handlers) GetCloneStatus(ctx echo.Context, id uuid.UUID) error { - userID, err := h.server.getIdentity(ctx) - if err != nil { - return err - } - - cloneEntry, err := h.server.db.GetClone(ctx.Request().Context(), id, userID.OrgID()) - if err != nil { - if errors.Is(err, db.ErrCloneNotFound) { - return echo.NewHTTPError(http.StatusNotFound, err) - } - ctx.Logger().Errorf("Error querying clone %v: %v", id, err) - return echo.NewHTTPError(http.StatusInternalServerError, "Something went wrong querying this clone") - } - if cloneEntry == nil { - return echo.NewHTTPError(http.StatusBadRequest, "Requested clone cannot be found") - } - - resp, err := h.server.cClient.CloneStatus(ctx.Request().Context(), id) - if err != nil { - ctx.Logger().Errorf("Error requesting clone status for clone %v: %v", id, err) - return err - } - defer closeBody(ctx, resp.Body) - if resp.StatusCode != http.StatusOK { - var cErr composer.Error - err = json.NewDecoder(resp.Body).Decode(&cErr) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Unable to parse composer error") - } - return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Unable to create clone job: %v", cErr.Reason)) - } - - var cloudStat composer.CloneStatus - err = json.NewDecoder(resp.Body).Decode(&cloudStat) - if err != nil { - ctx.Logger().Errorf("unable to decode clone status: %v", err) - return err - } - - var options CloneStatusResponse_Options - uo, err := cloudStat.Options.AsAWSEC2UploadStatus() - if err != nil { - ctx.Logger().Errorf("unable to decode clone status: %v", err) - return err - } - - err = options.FromAWSUploadStatus(AWSUploadStatus{ - Ami: uo.Ami, - Region: uo.Region, - }) - if err != nil { - ctx.Logger().Errorf("unable to encode clone status: %v", err) - return err - } - - return ctx.JSON(http.StatusOK, CloneStatusResponse{ - ComposeId: &cloneEntry.ComposeId, - Status: CloneStatusResponseStatus(cloudStat.Status), - Type: UploadTypes(cloudStat.Type), - Options: options, - }) -} - -func (h *Handlers) GetComposeClones(ctx echo.Context, composeId uuid.UUID, params GetComposeClonesParams) error { - err := h.canUserAccessComposeId(ctx, composeId) - if err != nil { - return err - } - - userID, err := h.server.getIdentity(ctx) - if err != nil { - return err - } - - limit := 100 - if params.Limit != nil && *params.Limit > 0 { - limit = *params.Limit - } - - offset := 0 - if params.Offset != nil { - offset = *params.Offset - } - - cloneEntries, count, err := h.server.db.GetClonesForCompose(ctx.Request().Context(), composeId, userID.OrgID(), limit, offset) - if err != nil { - ctx.Logger().Errorf("Error querying clones for compose %v: %v", composeId, err) - return echo.NewHTTPError(http.StatusInternalServerError, "Something went wrong querying clones for this compose") - } - - data := []ClonesResponseItem{} - for _, c := range cloneEntries { - var cr CloneRequest - err = json.Unmarshal(c.Request, &cr) - if err != nil { - return echo.NewHTTPError( - http.StatusInternalServerError, "Something went wrong querying clones for this compose") - } - data = append(data, ClonesResponseItem{ - Id: c.Id, - ComposeId: composeId, - Request: cr, - CreatedAt: c.CreatedAt.Format(time.RFC3339), - }) - } - - return ctx.JSON(http.StatusOK, ClonesResponse{ - Meta: ListResponseMeta{count}, - Links: h.newLinksWithExtraParams(fmt.Sprintf("composes/%v/clones", composeId), count, limit, url.Values{}), - Data: data, - }) -} - func closeBody(ctx echo.Context, body io.Closer) { err := body.Close() if err != nil { diff --git a/internal/v1/handler_get_compose_status_test.go b/internal/v1/handler_get_compose_status_test.go index cd0093d11..a97ee8d55 100644 --- a/internal/v1/handler_get_compose_status_test.go +++ b/internal/v1/handler_get_compose_status_test.go @@ -136,7 +136,7 @@ func TestComposeStatus(t *testing.T) { imageStatus: v1.ImageStatus{ Status: v1.ImageStatusStatusSuccess, UploadStatus: &v1.UploadStatus{ - Status: v1.Success, + Status: v1.UploadStatusStatusSuccess, Type: v1.UploadTypesAws, Options: ibAwsUS, }, @@ -165,7 +165,7 @@ func TestComposeStatus(t *testing.T) { imageStatus: v1.ImageStatus{ Status: v1.ImageStatusStatusSuccess, UploadStatus: &v1.UploadStatus{ - Status: v1.Success, + Status: v1.UploadStatusStatusSuccess, Type: v1.UploadTypesAwsS3, Options: ibAwsS3US, }, @@ -186,7 +186,7 @@ func TestComposeStatus(t *testing.T) { imageStatus: v1.ImageStatus{ Status: v1.ImageStatusStatusSuccess, UploadStatus: &v1.UploadStatus{ - Status: v1.Success, + Status: v1.UploadStatusStatusSuccess, Type: v1.UploadTypesAzure, Options: ibAzureUS, }, @@ -207,7 +207,7 @@ func TestComposeStatus(t *testing.T) { imageStatus: v1.ImageStatus{ Status: v1.ImageStatusStatusSuccess, UploadStatus: &v1.UploadStatus{ - Status: v1.Success, + Status: v1.UploadStatusStatusSuccess, Type: v1.UploadTypesGcp, Options: ibGcpUS, }, @@ -228,7 +228,7 @@ func TestComposeStatus(t *testing.T) { imageStatus: v1.ImageStatus{ Status: v1.ImageStatusStatusSuccess, UploadStatus: &v1.UploadStatus{ - Status: v1.Success, + Status: v1.UploadStatusStatusSuccess, Type: v1.UploadTypesOciObjectstorage, Options: ibOciUS, }, diff --git a/internal/v1/handler_test.go b/internal/v1/handler_test.go index ccd477232..bce6b9306 100644 --- a/internal/v1/handler_test.go +++ b/internal/v1/handler_test.go @@ -19,7 +19,6 @@ import ( "github.com/stretchr/testify/require" "github.com/osbuild/image-builder-crc/internal/clients/composer" - "github.com/osbuild/image-builder-crc/internal/clients/provisioning" "github.com/osbuild/image-builder-crc/internal/common" "github.com/osbuild/image-builder-crc/internal/tutils" v1 "github.com/osbuild/image-builder-crc/internal/v1" @@ -461,194 +460,6 @@ func TestMetrics(t *testing.T) { require.Contains(t, body, "image_builder_crc_compose_errors") } -func TestGetClones(t *testing.T) { - ctx := context.Background() - id := uuid.New() - cloneId := uuid.New() - awsAccountId := "123456123456" - - apiSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("Authorization") == "Bearer" { - w.WriteHeader(http.StatusUnauthorized) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - var cloneReq composer.AWSEC2CloneCompose - err := json.NewDecoder(r.Body).Decode(&cloneReq) - require.NoError(t, err) - require.Equal(t, awsAccountId, (*cloneReq.ShareWithAccounts)[0]) - - result := composer.CloneComposeResponse{ - Id: cloneId, - } - err = json.NewEncoder(w).Encode(result) - require.NoError(t, err) - })) - defer apiSrv.Close() - - provSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - awsId := struct { - AccountId *string `json:"account_id,omitempty"` - }{ - AccountId: &awsAccountId, - } - result := provisioning.V1SourceUploadInfoResponse{ - Aws: &awsId, - } - - require.Equal(t, tutils.AuthString0, r.Header.Get("x-rh-identity")) - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - err := json.NewEncoder(w).Encode(result) - require.NoError(t, err) - })) - defer provSrv.Close() - - srv := startServer(t, &testServerClientsConf{ComposerURL: apiSrv.URL, ProvURL: provSrv.URL}, &v1.ServerConfig{ - DistributionsDir: "../../distributions", - }) - defer srv.Shutdown(t) - - err := srv.DB.InsertCompose(ctx, id, "500000", "user500000@test.test", "000000", nil, json.RawMessage(` -{ - "image_requests": [ - { - "image_type": "aws" - } - ] -}`), nil, nil) - require.NoError(t, err) - - var csResp v1.ClonesResponse - respStatusCode, body := tutils.GetResponseBody(t, srv.URL+fmt.Sprintf("/api/image-builder/v1/composes/%s/clones", id), &tutils.AuthString0) - require.Equal(t, http.StatusOK, respStatusCode) - err = json.Unmarshal([]byte(body), &csResp) - require.NoError(t, err) - require.Equal(t, 0, len(csResp.Data)) - require.Contains(t, body, "\"data\":[]") - - cloneReq := v1.AWSEC2Clone{ - Region: "us-east-2", - ShareWithSources: &[]string{"1"}, - } - respStatusCode, body = tutils.PostResponseBody(t, srv.URL+fmt.Sprintf("/api/image-builder/v1/composes/%s/clone", id), cloneReq) - require.Equal(t, http.StatusCreated, respStatusCode) - - var cResp v1.CloneResponse - err = json.Unmarshal([]byte(body), &cResp) - require.NoError(t, err) - require.Equal(t, cloneId, cResp.Id) - - respStatusCode, body = tutils.GetResponseBody(t, srv.URL+fmt.Sprintf("/api/image-builder/v1/composes/%s/clones", id), &tutils.AuthString0) - require.Equal(t, http.StatusOK, respStatusCode) - err = json.Unmarshal([]byte(body), &csResp) - require.NoError(t, err) - require.Equal(t, 1, len(csResp.Data)) - require.Equal(t, cloneId, csResp.Data[0].Id) - - cloneReqExp, err := json.Marshal(cloneReq) - require.NoError(t, err) - cloneReqRecv, err := json.Marshal(csResp.Data[0].Request) - require.NoError(t, err) - require.Equal(t, cloneReqExp, cloneReqRecv) -} - -func TestGetCloneStatus(t *testing.T) { - ctx := context.Background() - cloneId := uuid.New() - id := uuid.New() - apiSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("Authorization") == "Bearer" { - w.WriteHeader(http.StatusUnauthorized) - return - } - w.Header().Set("Content-Type", "application/json") - - if strings.HasSuffix(r.URL.Path, fmt.Sprintf("/clones/%v", cloneId)) && r.Method == "GET" { - w.WriteHeader(http.StatusOK) - var uo composer.CloneStatus_Options - require.NoError(t, uo.FromAWSEC2UploadStatus(composer.AWSEC2UploadStatus{ - Ami: "ami-1", - Region: "us-east-2", - })) - result := composer.CloneStatus{ - Options: uo, - Status: composer.Success, - Type: composer.UploadTypesAws, - } - err := json.NewEncoder(w).Encode(result) - require.NoError(t, err) - } else if strings.HasSuffix(r.URL.Path, fmt.Sprintf("%v/clone", id)) && r.Method == "POST" { - w.WriteHeader(http.StatusCreated) - result := composer.CloneComposeResponse{ - Id: cloneId, - } - err := json.NewEncoder(w).Encode(result) - require.NoError(t, err) - } else { - require.FailNowf(t, "Unexpected request to mocked composer, path: %s", r.URL.Path) - } - })) - defer apiSrv.Close() - - srv := startServer(t, &testServerClientsConf{ComposerURL: apiSrv.URL}, &v1.ServerConfig{ - DistributionsDir: "../../distributions", - }) - defer srv.Shutdown(t) - - err := srv.DB.InsertCompose(ctx, id, "500000", "user500000@test.test", "000000", nil, json.RawMessage(` -{ - "image_requests": [ - { - "image_type": "aws" - } - ] -}`), nil, nil) - require.NoError(t, err) - - cloneReq := v1.AWSEC2Clone{ - Region: "us-east-2", - } - respStatusCode, body := tutils.PostResponseBody(t, srv.URL+fmt.Sprintf("/api/image-builder/v1/composes/%s/clone", id), cloneReq) - require.Equal(t, http.StatusCreated, respStatusCode) - - var cResp v1.CloneResponse - err = json.Unmarshal([]byte(body), &cResp) - require.NoError(t, err) - require.Equal(t, cloneId, cResp.Id) - - var usResp v1.CloneStatusResponse - respStatusCode, body = tutils.GetResponseBody(t, srv.URL+fmt.Sprintf("/api/image-builder/v1/clones/%s", cloneId), &tutils.AuthString0) - - require.Equal(t, http.StatusOK, respStatusCode) - err = json.Unmarshal([]byte(body), &usResp) - require.NoError(t, err) - require.Equal(t, v1.CloneStatusResponseStatusSuccess, usResp.Status) - require.Equal(t, v1.UploadTypesAws, usResp.Type) - require.Equal(t, id, *usResp.ComposeId) - - var awsUS v1.AWSUploadStatus - jsonUO, err := json.Marshal(usResp.Options) - require.NoError(t, err) - err = json.Unmarshal(jsonUO, &awsUS) - require.NoError(t, err) - require.Equal(t, "ami-1", awsUS.Ami) - require.Equal(t, "us-east-2", awsUS.Region) -} - -func TestGetCloneEntryNotFoundResponse(t *testing.T) { - id := uuid.New().String() - srv := startServer(t, &testServerClientsConf{}, nil) - defer srv.Shutdown(t) - - respStatusCode, body := tutils.GetResponseBody(t, srv.URL+fmt.Sprintf("/api/image-builder/v1/clones/%s", - id), &tutils.AuthString0) - require.Equal(t, http.StatusNotFound, respStatusCode) - require.Contains(t, body, "clone not found") -} - func TestValidateSpec(t *testing.T) { spec, err := v1.GetSwagger() require.NoError(t, err)