Skip to content

Commit

Permalink
fix(HMS-2757): Do not cascade on pubkey delete
Browse files Browse the repository at this point in the history
  • Loading branch information
avitova authored and ezr-ondrej committed Oct 20, 2023
1 parent 0479f94 commit f9724b4
Show file tree
Hide file tree
Showing 16 changed files with 70 additions and 28 deletions.
12 changes: 6 additions & 6 deletions cmd/spec/example_reservation.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ var AwsReservationRequestPayloadExample = payloads.AWSReservationRequest{
}

var AwsReservationResponsePayloadPendingExample = payloads.AWSReservationResponse{
PubkeyID: 42,
PubkeyID: ptr.ToInt64(42),
SourceID: "654321",
Region: "us-east-1",
InstanceType: "t3.small",
Expand All @@ -99,7 +99,7 @@ var AwsReservationResponsePayloadPendingExample = payloads.AWSReservationRespons

var AwsReservationResponsePayloadDoneExample = payloads.AWSReservationResponse{
ID: 1305,
PubkeyID: 42,
PubkeyID: ptr.ToInt64(42),
SourceID: "654321",
Region: "us-east-1",
InstanceType: "t3.small",
Expand Down Expand Up @@ -133,7 +133,7 @@ var AzureReservationRequestPayloadExample = payloads.AzureReservationRequest{

var AzureReservationResponsePayloadPendingExample = payloads.AzureReservationResponse{
ID: 1310,
PubkeyID: 42,
PubkeyID: ptr.ToInt64(42),
SourceID: "654321",
ResourceGroup: "myCustom Azure RG",
Location: "useast",
Expand All @@ -147,7 +147,7 @@ var AzureReservationResponsePayloadPendingExample = payloads.AzureReservationRes

var AzureReservationResponsePayloadDoneExample = payloads.AzureReservationResponse{
ID: 1310,
PubkeyID: 42,
PubkeyID: ptr.ToInt64(42),
SourceID: "654321",
ResourceGroup: "myCustom Azure RG",
Location: "useast",
Expand Down Expand Up @@ -179,7 +179,7 @@ var GCPReservationRequestPayloadExample = payloads.GCPReservationRequest{

var GCPReservationResponsePayloadPendingExample = payloads.GCPReservationResponse{
ID: 1305,
PubkeyID: 42,
PubkeyID: ptr.ToInt64(42),
SourceID: "654321",
Zone: "us-east-4",
MachineType: "e2-micro",
Expand All @@ -193,7 +193,7 @@ var GCPReservationResponsePayloadPendingExample = payloads.GCPReservationRespons

var GCPReservationResponsePayloadDoneExample = payloads.GCPReservationResponse{
ID: 1305,
PubkeyID: 42,
PubkeyID: ptr.ToInt64(42),
SourceID: "654321",
Zone: "us-east-4",
MachineType: "e2-micro",
Expand Down
3 changes: 3 additions & 0 deletions internal/dao/dao_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ var (

// ErrReservationRateExceeded is returned when SQL constraint does not allow to insert more reservations
ErrReservationRateExceeded = usrerr.New(429, "rate limit exceeded", "too many reservations, wait and retry")

// ErrPubkeyNotFound is returned when a nil pointer to a pubkey is used for reservation detail
ErrPubkeyNotFound = usrerr.New(404, "pubkey not found", "no pubkey found, it may have been already deleted")
)
14 changes: 13 additions & 1 deletion internal/dao/pgx/reservation_pgx.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,15 @@ func (x *reservationDao) CreateAWS(ctx context.Context, reservation *models.AWSR
return err
}

if reservation.PubkeyID == nil {
return fmt.Errorf("pgx error: %w", dao.ErrPubkeyNotFound)
}

awsQuery := `INSERT INTO aws_reservation_details (reservation_id, pubkey_id, source_id, image_id, detail)
VALUES ($1, $2, $3, $4, $5)`
tag, err := tx.Exec(ctx, awsQuery,
reservation.ID,
reservation.PubkeyID,
&reservation.PubkeyID,
reservation.SourceID,
reservation.ImageID,
reservation.Detail)
Expand All @@ -80,6 +84,10 @@ func (x *reservationDao) CreateAzure(ctx context.Context, reservation *models.Az
return err
}

if reservation.PubkeyID == nil {
return fmt.Errorf("pgx error: %w", dao.ErrPubkeyNotFound)
}

azureQuery := `INSERT INTO azure_reservation_details (reservation_id, pubkey_id, source_id, image_id, detail)
VALUES ($1, $2, $3, $4, $5)`
tag, err := tx.Exec(ctx, azureQuery,
Expand Down Expand Up @@ -111,6 +119,10 @@ func (x *reservationDao) CreateGCP(ctx context.Context, reservation *models.GCPR
return err
}

if reservation.PubkeyID == nil {
return fmt.Errorf("pgx error: %w", dao.ErrPubkeyNotFound)
}

gcpQuery := `INSERT INTO gcp_reservation_details (reservation_id, pubkey_id, source_id, image_id, detail)
VALUES ($1, $2, $3, $4, $5)`
tag, err := tx.Exec(ctx, gcpQuery,
Expand Down
5 changes: 3 additions & 2 deletions internal/dao/tests/reservation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/RHEnVision/provisioning-backend/internal/dao"
"github.com/RHEnVision/provisioning-backend/internal/db"
"github.com/RHEnVision/provisioning-backend/internal/models"
"github.com/RHEnVision/provisioning-backend/internal/ptr"
"github.com/RHEnVision/provisioning-backend/internal/testing/identity"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -47,7 +48,7 @@ func newAWSReservation() *models.AWSReservation {
AccountID: 1,
Status: "Created",
},
PubkeyID: 1,
PubkeyID: ptr.ToInt64(1),
}
}

Expand All @@ -58,7 +59,7 @@ func newGCPReservation() *models.GCPReservation {
AccountID: 1,
Status: "Created",
},
PubkeyID: 1,
PubkeyID: ptr.ToInt64(1),
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/jobs/launch_instance_aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func prepareAWSReservation(t *testing.T, ctx context.Context, pk *models.Pubkey)
PowerOff: false,
}
reservation := &models.AWSReservation{
PubkeyID: pk.ID,
PubkeyID: &pk.ID,
SourceID: "irrelevant",
ImageID: "irrelevant",
Detail: detail,
Expand Down
2 changes: 1 addition & 1 deletion internal/jobs/launch_instance_azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func prepareAzureReservation(t *testing.T, ctx context.Context, pk *models.Pubke
PowerOff: false,
}
reservation := &models.AzureReservation{
PubkeyID: pk.ID,
PubkeyID: &pk.ID,
SourceID: "irrelevant",
ImageID: "irrelevant",
Detail: detail,
Expand Down
2 changes: 1 addition & 1 deletion internal/jobs/launch_instance_gcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func prepareGCPReservation(t *testing.T, ctx context.Context, pk *models.Pubkey)
PowerOff: false,
}
reservation := &models.GCPReservation{
PubkeyID: pk.ID,
PubkeyID: &pk.ID,
SourceID: "irrelevant",
ImageID: "irrelevant",
Detail: detail,
Expand Down
14 changes: 14 additions & 0 deletions internal/migrations/sql/021_remove_delete_cascade.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ALTER TABLE aws_reservation_details
DROP CONSTRAINT aws_reservation_details_pubkey_id_fkey,
ADD CONSTRAINT aws_reservation_details_pubkey_id_fkey
FOREIGN key (pubkey_id) REFERENCES pubkeys(id) ON DELETE SET NULL;

ALTER TABLE gcp_reservation_details
DROP CONSTRAINT gcp_reservation_details_pubkey_id_fkey,
ADD CONSTRAINT gcp_reservation_details_pubkey_id_fkey
FOREIGN key (pubkey_id) REFERENCES pubkeys(id) ON DELETE SET NULL;

ALTER TABLE azure_reservation_details
DROP CONSTRAINT azure_reservation_details_pubkey_id_fkey,
ADD CONSTRAINT azure_reservation_details_pubkey_id_fkey
FOREIGN key (pubkey_id) REFERENCES pubkeys(id) ON DELETE SET NULL;
3 changes: 3 additions & 0 deletions internal/migrations/sql/022_pubkey_nullable.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE aws_reservation_details ALTER COLUMN pubkey_id DROP NOT NULL;
ALTER TABLE gcp_reservation_details ALTER COLUMN pubkey_id DROP NOT NULL;
ALTER TABLE azure_reservation_details ALTER COLUMN pubkey_id DROP NOT NULL;
6 changes: 3 additions & 3 deletions internal/models/reservation_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ type AWSReservation struct {
Reservation

// Pubkey ID.
PubkeyID int64 `db:"pubkey_id" json:"pubkey_id"`
PubkeyID *int64 `db:"pubkey_id" json:"pubkey_id,omitempty"`

// Source ID.
SourceID string `db:"source_id" json:"source_id"`
Expand Down Expand Up @@ -118,7 +118,7 @@ type GCPReservation struct {
Reservation

// Pubkey ID.
PubkeyID int64 `db:"pubkey_id" json:"pubkey_id"`
PubkeyID *int64 `db:"pubkey_id" json:"pubkey_id,omitempty"`

// Source ID.
SourceID string `db:"source_id" json:"source_id"`
Expand Down Expand Up @@ -158,7 +158,7 @@ type AzureReservation struct {
Reservation

// Pubkey ID.
PubkeyID int64 `db:"pubkey_id" json:"pubkey_id"`
PubkeyID *int64 `db:"pubkey_id" json:"pubkey_id,omitempty"`

// Source ID.
SourceID string `db:"source_id" json:"source_id"`
Expand Down
6 changes: 3 additions & 3 deletions internal/payloads/reservation_payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type AWSReservationResponse struct {
ID int64 `json:"reservation_id" yaml:"reservation_id"`

// Pubkey ID.
PubkeyID int64 `json:"pubkey_id" yaml:"pubkey_id"`
PubkeyID *int64 `json:"pubkey_id,omitempty" yaml:"pubkey_id,omitempty"`

// Source ID.
SourceID string `json:"source_id" yaml:"source_id"`
Expand Down Expand Up @@ -91,7 +91,7 @@ type AWSReservationResponse struct {
type AzureReservationResponse struct {
ID int64 `json:"reservation_id" yaml:"reservation_id"`

PubkeyID int64 `json:"pubkey_id" yaml:"pubkey_id"`
PubkeyID *int64 `json:"pubkey_id,omitempty" yaml:"pubkey_id,omitempty"`

SourceID string `json:"source_id" yaml:"source_id"`

Expand Down Expand Up @@ -123,7 +123,7 @@ type GCPReservationResponse struct {
ID int64 `json:"reservation_id" yaml:"reservation_id"`

// Pubkey ID.
PubkeyID int64 `json:"pubkey_id" yaml:"pubkey_id"`
PubkeyID *int64 `json:"pubkey_id,omitempty" yaml:"pubkey_id,omitempty"`

// Source ID.
SourceID string `json:"source_id" yaml:"source_id"`
Expand Down
10 changes: 7 additions & 3 deletions internal/services/aws_reservation_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func CreateAWSReservation(w http.ResponseWriter, r *http.Request) {
PowerOff: payload.PowerOff,
}
reservation := &models.AWSReservation{
PubkeyID: payload.PubkeyID,
PubkeyID: &payload.PubkeyID,
SourceID: payload.SourceID,
ImageID: payload.ImageID,
Detail: detail,
Expand All @@ -89,8 +89,12 @@ func CreateAWSReservation(w http.ResponseWriter, r *http.Request) {
reservation.Detail.Name = newName

// validate pubkey - must be always present because of data integrity (foreign keys)
logger.Debug().Msgf("Validating existence of pubkey %d for this account", reservation.PubkeyID)
pk, err := pkDao.GetById(r.Context(), reservation.PubkeyID)
if reservation.PubkeyID == nil {
renderError(w, r, payloads.NewNotFoundError(r.Context(), "could not create AWS reservation", ErrPubkeyNotFound))
}

logger.Debug().Msgf("Validating existence of pubkey %d for this account", *reservation.PubkeyID)
pk, err := pkDao.GetById(r.Context(), *reservation.PubkeyID)
if err != nil {
message := fmt.Sprintf("get pubkey with id %d", reservation.PubkeyID)
renderNotFoundOrDAOError(w, r, err, message)
Expand Down
2 changes: 1 addition & 1 deletion internal/services/azure_reservation_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func CreateAzureReservation(w http.ResponseWriter, r *http.Request) {
Name: name,
}
reservation := &models.AzureReservation{
PubkeyID: payload.PubkeyID,
PubkeyID: &payload.PubkeyID,
SourceID: payload.SourceID,
ImageID: payload.ImageID,
Detail: detail,
Expand Down
14 changes: 9 additions & 5 deletions internal/services/gcp_reservation_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func CreateGCPReservation(w http.ResponseWriter, r *http.Request) {
LaunchTemplateID: payload.LaunchTemplateID,
}
reservation := &models.GCPReservation{
PubkeyID: payload.PubkeyID,
PubkeyID: &payload.PubkeyID,
ImageID: payload.ImageID,
SourceID: payload.SourceID,
Detail: detail,
Expand All @@ -79,10 +79,14 @@ func CreateGCPReservation(w http.ResponseWriter, r *http.Request) {
reservation.Steps = 2
reservation.StepTitles = jobs.LaunchInstanceGCPSteps

logger.Debug().Msgf("Validating existence of pubkey %d for this account", reservation.PubkeyID)
pk, err := pkDao.GetById(r.Context(), reservation.PubkeyID)
if reservation.PubkeyID == nil {
renderError(w, r, payloads.NewNotFoundError(r.Context(), "could not create AWS reservation", ErrPubkeyNotFound))
}

logger.Debug().Msgf("Validating existence of pubkey %d for this account", *reservation.PubkeyID)
pk, err := pkDao.GetById(r.Context(), *reservation.PubkeyID)
if err != nil {
message := fmt.Sprintf("get pubkey with id %d", reservation.PubkeyID)
message := fmt.Sprintf("get pubkey with id %d", *reservation.PubkeyID)
renderNotFoundOrDAOError(w, r, err, message)
return
}
Expand Down Expand Up @@ -147,7 +151,7 @@ func CreateGCPReservation(w http.ResponseWriter, r *http.Request) {
Args: jobs.LaunchInstanceGCPTaskArgs{
ReservationID: reservation.ID,
Zone: reservation.Detail.Zone,
PubkeyID: reservation.PubkeyID,
PubkeyID: *reservation.PubkeyID,
Detail: reservation.Detail,
ImageName: name,
ProjectID: authentication,
Expand Down
1 change: 1 addition & 0 deletions internal/services/reservations_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var (
ErrBothTypeAndTemplateMissing = errors.New("instance type or launch template not set")
ErrUnsupportedRegion = errors.New("unknown region/location/zone")
ErrInvalidNamePattern = errors.New("name pattern is not RFC-1035 compatible")
ErrPubkeyNotFound = errors.New("no pubkey found")
)

// CreateReservation dispatches requests to type provider specific handlers
Expand Down
2 changes: 1 addition & 1 deletion internal/services/reservations_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestGetReservationDetail(t *testing.T) {
PowerOff: true,
}
reservation := &models.AWSReservation{
PubkeyID: pk.ID,
PubkeyID: &pk.ID,
SourceID: "1",
ImageID: "ami-random",
Detail: detail,
Expand Down

0 comments on commit f9724b4

Please sign in to comment.