Skip to content

Commit

Permalink
Feature: Commitments support for Azure's reservations (#302)
Browse files Browse the repository at this point in the history
* types preparation

* more mapping impls for azure

* working import + update

* fix getReservationResources

* add term mapping to MapReservationImportToResource to mimic API's behavior

* fix tests

* TestMapCommitmentToReservationResource

* rename

* mapping functions tests

* working first step of acc test

* CheckDestroy

* working state import step

* working azure acceptance tests

* get rid of the dummy test

* add usage examples

* fix populateCommitmentsResourceData

* full acc tests

* get rid of productname from the matcher

* flatten matcher

* regenerate docs

* format tf

* regenerate docs

* add descriptions

* MapConfigsToCommitments impl

* regenerate docs

* newline

* return err from MapReservationImportToResource

* get rid of attr schema config

* use common fields for cast ai commitment fields

* flatten the structure, get rid of the separate commitments package

* regenerate sdk

* renames

* renames

* dont export mapping functions

* dont export field names

* Feature: Commitment assignments support (#304)

* working assignments

* add example

* adjust tests

* more test adjustments

* add config nil check

* priority computed field

* regenerate docs

* fix replacing assignments

* working acceptance tests with assignments for gke

* add assignment checks

* azure assignments acc tests

* rearrange acc tests

* use common fields for cast ai commitment fields

* flatten the structure, get rid of the separate commitments package

* renames

* renames

* imports fix

* renames

* dont export field names

---------

Co-authored-by: Max Twardowski <[email protected]>

---------

Co-authored-by: Max Twardowski <[email protected]>
  • Loading branch information
maxtwardowski and Max Twardowski authored May 7, 2024
1 parent 2e38acf commit 2649d00
Show file tree
Hide file tree
Showing 11 changed files with 2,132 additions and 635 deletions.
65 changes: 30 additions & 35 deletions castai/reservations/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,37 @@ const (
FieldReservationExpirationDate = "expiration_date"
FieldReservationType = "type"
FieldReservationDeepLinkToReservation = "deep_link_to_reservation"
FieldReservationID = "reservation_id"
FieldReservationScopeResourceGroup = "scope_resource_group"
FieldReservationScopeSubscription = "scope_subscription"
FieldReservationScopeStatus = "scope_status"
FieldReservationScope = "scope"
FieldReservationTerm = "term"
FieldReservationStatus = "status"
)

var reservationResourceFields = []string{
FieldReservationName,
FieldReservationProvider,
FieldReservationRegion,
FieldReservationInstanceType,
FieldReservationPrice,
FieldReservationCount,
FieldReservationStartDate,
FieldReservationEndDate,
FieldReservationZoneId,
FieldReservationZoneName,
FieldReservationProductName,
FieldReservationQuantity,
FieldReservationPurchaseDate,
FieldReservationExpirationDate,
FieldReservationType,
FieldReservationDeepLinkToReservation,
}

var csvColumnAlias = map[string][]string{
FieldReservationName: {FieldReservationName},
FieldReservationProvider: {FieldReservationProvider},
FieldReservationRegion: {FieldReservationRegion},
FieldReservationInstanceType: {FieldReservationInstanceType, FieldReservationProductName},
FieldReservationPrice: {FieldReservationPrice},
FieldReservationCount: {FieldReservationCount, FieldReservationQuantity},
FieldReservationStartDate: {FieldReservationStartDate, FieldReservationPurchaseDate},
FieldReservationEndDate: {FieldReservationEndDate, FieldReservationExpirationDate},
FieldReservationZoneId: {FieldReservationZoneId},
FieldReservationZoneName: {FieldReservationZoneName},
FieldReservationProductName: {FieldReservationProductName},
FieldReservationQuantity: {FieldReservationQuantity},
FieldReservationPurchaseDate: {FieldReservationPurchaseDate},
FieldReservationExpirationDate: {FieldReservationExpirationDate},
FieldReservationType: {FieldReservationType},
FieldReservationDeepLinkToReservation: {FieldReservationDeepLinkToReservation},
FieldReservationName: {},
FieldReservationProvider: {},
FieldReservationRegion: {},
FieldReservationInstanceType: {FieldReservationProductName},
FieldReservationPrice: {},
FieldReservationCount: {FieldReservationQuantity},
FieldReservationStartDate: {FieldReservationPurchaseDate},
FieldReservationEndDate: {FieldReservationExpirationDate},
FieldReservationZoneId: {},
FieldReservationZoneName: {},
FieldReservationProductName: {},
FieldReservationQuantity: {},
FieldReservationPurchaseDate: {},
FieldReservationExpirationDate: {},
FieldReservationType: {},
FieldReservationDeepLinkToReservation: {},
FieldReservationID: {},
FieldReservationScopeResourceGroup: {},
FieldReservationScopeSubscription: {},
FieldReservationScopeStatus: {},
FieldReservationTerm: {},
FieldReservationStatus: {},
FieldReservationScope: {},
}
57 changes: 29 additions & 28 deletions castai/reservations/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package reservations

import (
"fmt"
"github.com/castai/terraform-provider-castai/castai/sdk"
"github.com/samber/lo"
"strconv"
"strings"
"time"

"github.com/samber/lo"

"github.com/castai/terraform-provider-castai/castai/sdk"
)

type ReservationResource map[string]*string
Expand All @@ -21,7 +23,7 @@ func MapCsvRecordsToReservationResources(csvRecords [][]string) ([]*ReservationR
})

reservationRecords := csvRecords[1:]
fieldIndexes := mapReservationsHeaderToReservationFieldIndexes(normalizedCsvColumnNames)
fieldIndexes := MapReservationsHeaderToReservationFieldIndexes(normalizedCsvColumnNames)

reservations := make([]*ReservationResource, 0, len(reservationRecords))
for _, record := range reservationRecords {
Expand Down Expand Up @@ -88,12 +90,11 @@ func MapToReservationResourcesWithCommonFieldsOnly(reservationResources []*Reser
})
}

func mapReservationsHeaderToReservationFieldIndexes(columns []string) map[string]int {
indexes := make(map[string]int, len(reservationResourceFields))
for _, field := range reservationResourceFields {
func MapReservationsHeaderToReservationFieldIndexes(columns []string) map[string]int {
indexes := make(map[string]int, len(csvColumnAlias))
for field, aliases := range csvColumnAlias {
index := -1
aliases := csvColumnAlias[field]
for _, alias := range aliases {
for _, alias := range append([]string{field}, aliases...) {
_, fieldIdx, found := lo.FindIndexOf(columns, func(item string) bool {
return strings.ToLower(item) == alias
})
Expand All @@ -110,46 +111,46 @@ func mapReservationsHeaderToReservationFieldIndexes(columns []string) map[string
}

func mapRecordToReservationResource(fieldIndexes map[string]int, record []string) (*ReservationResource, error) {
provider, err := getRecordReservationProvider(fieldIndexes, record)
provider, err := GetRecordReservationProvider(fieldIndexes, record)
if err != nil {
return nil, err
}

return &ReservationResource{
FieldReservationName: getRecordFieldStringValue(FieldReservationName, fieldIndexes, record),
FieldReservationName: GetRecordFieldStringValue(FieldReservationName, fieldIndexes, record),
FieldReservationProvider: provider,
FieldReservationRegion: getRecordFieldStringValue(FieldReservationRegion, fieldIndexes, record),
FieldReservationInstanceType: getRecordFieldStringValue(FieldReservationInstanceType, fieldIndexes, record),
FieldReservationPrice: getRecordFieldStringValue(FieldReservationPrice, fieldIndexes, record),
FieldReservationCount: getRecordFieldStringValue(FieldReservationCount, fieldIndexes, record),
FieldReservationStartDate: getRecordFieldStringValue(FieldReservationStartDate, fieldIndexes, record),
FieldReservationEndDate: getRecordFieldStringValue(FieldReservationEndDate, fieldIndexes, record),
FieldReservationZoneId: getRecordFieldStringValue(FieldReservationZoneId, fieldIndexes, record),
FieldReservationZoneName: getRecordFieldStringValue(FieldReservationZoneName, fieldIndexes, record),
FieldReservationProductName: getRecordFieldStringValue(FieldReservationProductName, fieldIndexes, record),
FieldReservationQuantity: getRecordFieldStringValue(FieldReservationQuantity, fieldIndexes, record),
FieldReservationPurchaseDate: getRecordFieldStringValue(FieldReservationPurchaseDate, fieldIndexes, record),
FieldReservationExpirationDate: getRecordFieldStringValue(FieldReservationExpirationDate, fieldIndexes, record),
FieldReservationType: getRecordFieldStringValue(FieldReservationType, fieldIndexes, record),
FieldReservationDeepLinkToReservation: getRecordFieldStringValue(FieldReservationDeepLinkToReservation, fieldIndexes, record),
FieldReservationRegion: GetRecordFieldStringValue(FieldReservationRegion, fieldIndexes, record),
FieldReservationInstanceType: GetRecordFieldStringValue(FieldReservationInstanceType, fieldIndexes, record),
FieldReservationPrice: GetRecordFieldStringValue(FieldReservationPrice, fieldIndexes, record),
FieldReservationCount: GetRecordFieldStringValue(FieldReservationCount, fieldIndexes, record),
FieldReservationStartDate: GetRecordFieldStringValue(FieldReservationStartDate, fieldIndexes, record),
FieldReservationEndDate: GetRecordFieldStringValue(FieldReservationEndDate, fieldIndexes, record),
FieldReservationZoneId: GetRecordFieldStringValue(FieldReservationZoneId, fieldIndexes, record),
FieldReservationZoneName: GetRecordFieldStringValue(FieldReservationZoneName, fieldIndexes, record),
FieldReservationProductName: GetRecordFieldStringValue(FieldReservationProductName, fieldIndexes, record),
FieldReservationQuantity: GetRecordFieldStringValue(FieldReservationQuantity, fieldIndexes, record),
FieldReservationPurchaseDate: GetRecordFieldStringValue(FieldReservationPurchaseDate, fieldIndexes, record),
FieldReservationExpirationDate: GetRecordFieldStringValue(FieldReservationExpirationDate, fieldIndexes, record),
FieldReservationType: GetRecordFieldStringValue(FieldReservationType, fieldIndexes, record),
FieldReservationDeepLinkToReservation: GetRecordFieldStringValue(FieldReservationDeepLinkToReservation, fieldIndexes, record),
}, nil
}

func getRecordReservationProvider(fieldIndexes map[string]int, record []string) (*string, error) {
provider := getRecordFieldStringValue(FieldReservationProvider, fieldIndexes, record)
func GetRecordReservationProvider(fieldIndexes map[string]int, record []string) (*string, error) {
provider := GetRecordFieldStringValue(FieldReservationProvider, fieldIndexes, record)
if provider != nil && *provider != "" {
return provider, nil
}

deepLinkToReservation := getRecordFieldStringValue(FieldReservationDeepLinkToReservation, fieldIndexes, record)
deepLinkToReservation := GetRecordFieldStringValue(FieldReservationDeepLinkToReservation, fieldIndexes, record)
if deepLinkToReservation != nil && strings.Contains(*deepLinkToReservation, "azure") {
return lo.ToPtr("azure"), nil
}

return nil, fmt.Errorf("reservation provider could not be determined: %v", record)
}

func getRecordFieldStringValue(field string, fieldIndexes map[string]int, record []string) *string {
func GetRecordFieldStringValue(field string, fieldIndexes map[string]int, record []string) *string {
index, found := fieldIndexes[field]
if !found || index == -1 {
return nil
Expand Down
4 changes: 2 additions & 2 deletions castai/reservations/mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func Test_mapReservationsHeaderToReservationFieldIndexes(t *testing.T) {
t.Run(testName, func(t *testing.T) {
r := require.New(t)

got := mapReservationsHeaderToReservationFieldIndexes(tt.args.columns)
got := MapReservationsHeaderToReservationFieldIndexes(tt.args.columns)

r.Equal(tt.want, got)
})
Expand Down Expand Up @@ -401,7 +401,7 @@ func Test_getRecordReservationProvider(t *testing.T) {
t.Run(testName, func(t *testing.T) {
r := require.New(t)

got, err := getRecordReservationProvider(tt.args.fieldIndexes, tt.args.record)
got, err := GetRecordReservationProvider(tt.args.fieldIndexes, tt.args.record)

if tt.expectErrMessageContains != nil {
r.Error(err)
Expand Down
Loading

0 comments on commit 2649d00

Please sign in to comment.