add event base intelligency

This commit is contained in:
mr
2026-01-13 16:04:31 +01:00
parent c35b06e0bc
commit 6d745fe922
46 changed files with 859 additions and 455 deletions

View File

@@ -57,6 +57,7 @@ const (
LIVE_DATACENTER = tools.LIVE_DATACENTER LIVE_DATACENTER = tools.LIVE_DATACENTER
LIVE_STORAGE = tools.LIVE_STORAGE LIVE_STORAGE = tools.LIVE_STORAGE
PURCHASE_RESOURCE = tools.PURCHASE_RESOURCE PURCHASE_RESOURCE = tools.PURCHASE_RESOURCE
NATIVE_TOOL = tools.NATIVE_TOOL
) )
// will turn into standards api hostnames // will turn into standards api hostnames
@@ -108,7 +109,7 @@ func InitDaemon(appName string) {
logs.SetLogger(logs.CreateLogger("main")) logs.SetLogger(logs.CreateLogger("main"))
// Load the right config file // Load the right config file
o := GetConfLoader() o := GetConfLoader()
resources.InitNative()
// feed the library with the loaded config // feed the library with the loaded config
SetConfig( SetConfig(
o.GetStringDefault("MONGO_URL", "mongodb://127.0.0.1:27017"), o.GetStringDefault("MONGO_URL", "mongodb://127.0.0.1:27017"),
@@ -265,8 +266,8 @@ func (r *Request) Schedule(wfID string, scheduler *workflow_execution.WorkflowSc
return ws, nil return ws, nil
} }
func (r *Request) CheckBooking(wfID string, start string, end string, durationInS float64, cron string) bool { func (r *Request) CheckBooking(wfID string, mode int, start string, end string, durationInS float64, cron string) bool {
ok, _, _, _, _, err := workflow_execution.NewScheduler(start, end, durationInS, cron).GetBuyAndBook(wfID, &tools.APIRequest{ ok, _, _, _, _, err := workflow_execution.NewScheduler(mode, start, end, durationInS, cron).GetBuyAndBook(wfID, &tools.APIRequest{
Caller: r.caller, Caller: r.caller,
Username: r.user, Username: r.user,
PeerID: r.peerID, PeerID: r.peerID,
@@ -598,7 +599,6 @@ func (l *LibData) ToPurchasedResource() *purchase_resource.PurchaseResource {
return nil return nil
} }
// ============== ADMIRALTY ============== // ============== ADMIRALTY ==============
// Returns a concatenation of the peerId and namespace in order for // Returns a concatenation of the peerId and namespace in order for
// kubernetes ressources to have a unique name, under 63 characters // kubernetes ressources to have a unique name, under 63 characters
@@ -614,48 +614,48 @@ func GetConcatenatedName(peerId string, namespace string) string {
func LoadOneStorage(storageId string, user string, peerID string, groups []string) (*resources.StorageResource, error) { func LoadOneStorage(storageId string, user string, peerID string, groups []string) (*resources.StorageResource, error) {
res := NewRequest(LibDataEnum(STORAGE_RESOURCE), user, peerID, groups,nil).LoadOne(storageId) res := NewRequest(LibDataEnum(STORAGE_RESOURCE), user, peerID, groups, nil).LoadOne(storageId)
if res.Code != 200 { if res.Code != 200 {
l := GetLogger() l := GetLogger()
l.Error().Msg("Error while loading storage ressource " + storageId) l.Error().Msg("Error while loading storage ressource " + storageId)
return nil,fmt.Errorf(res.Err) return nil, fmt.Errorf(res.Err)
} }
return res.ToStorageResource(), nil return res.ToStorageResource(), nil
} }
func LoadOneComputing(computingId string, user string, peerID string, groups []string) (*resources.ComputeResource, error) { func LoadOneComputing(computingId string, user string, peerID string, groups []string) (*resources.ComputeResource, error) {
res := NewRequest(LibDataEnum(COMPUTE_RESOURCE), user, peerID, groups,nil).LoadOne(computingId) res := NewRequest(LibDataEnum(COMPUTE_RESOURCE), user, peerID, groups, nil).LoadOne(computingId)
if res.Code != 200 { if res.Code != 200 {
l := GetLogger() l := GetLogger()
l.Error().Msg("Error while loading computing ressource " + computingId) l.Error().Msg("Error while loading computing ressource " + computingId)
return nil,fmt.Errorf(res.Err) return nil, fmt.Errorf(res.Err)
} }
return res.ToComputeResource(), nil return res.ToComputeResource(), nil
} }
func LoadOneProcessing(processingId string, user string, peerID string, groups []string) (*resources.ProcessingResource, error) { func LoadOneProcessing(processingId string, user string, peerID string, groups []string) (*resources.ProcessingResource, error) {
res := NewRequest(LibDataEnum(PROCESSING_RESOURCE), user, peerID, groups,nil).LoadOne(processingId) res := NewRequest(LibDataEnum(PROCESSING_RESOURCE), user, peerID, groups, nil).LoadOne(processingId)
if res.Code != 200 { if res.Code != 200 {
l := GetLogger() l := GetLogger()
l.Error().Msg("Error while loading processing ressource " + processingId) l.Error().Msg("Error while loading processing ressource " + processingId)
return nil,fmt.Errorf(res.Err) return nil, fmt.Errorf(res.Err)
} }
return res.ToProcessingResource(), nil return res.ToProcessingResource(), nil
} }
func LoadOneData(dataId string, user string, peerID string, groups []string) (*resources.DataResource, error) { func LoadOneData(dataId string, user string, peerID string, groups []string) (*resources.DataResource, error) {
res := NewRequest(LibDataEnum(DATA_RESOURCE), user, peerID, groups,nil).LoadOne(dataId) res := NewRequest(LibDataEnum(DATA_RESOURCE), user, peerID, groups, nil).LoadOne(dataId)
if res.Code != 200 { if res.Code != 200 {
l := GetLogger() l := GetLogger()
l.Error().Msg("Error while loading data ressource " + dataId) l.Error().Msg("Error while loading data ressource " + dataId)
return nil,fmt.Errorf(res.Err) return nil, fmt.Errorf(res.Err)
} }
return res.ToDataResource(), nil return res.ToDataResource(), nil
} }

View File

@@ -169,7 +169,7 @@ func (d *PeerOrder) Pay(request *tools.APIRequest, response chan *PeerOrder, wg
func (d *PeerOrder) SumUpBill(request *tools.APIRequest) error { func (d *PeerOrder) SumUpBill(request *tools.APIRequest) error {
for _, b := range d.Items { for _, b := range d.Items {
tot, err := b.GetPrice(request) // missing something tot, err := b.GetPriceHT(request) // missing something
if err != nil { if err != nil {
return err return err
} }
@@ -184,7 +184,7 @@ type PeerItemOrder struct {
Item map[string]interface{} `json:"item,omitempty" bson:"item,omitempty"` Item map[string]interface{} `json:"item,omitempty" bson:"item,omitempty"`
} }
func (d *PeerItemOrder) GetPrice(request *tools.APIRequest) (float64, error) { func (d *PeerItemOrder) GetPriceHT(request *tools.APIRequest) (float64, error) {
/////////// Temporary in order to allow GenerateOrder to complete while billing is still WIP /////////// Temporary in order to allow GenerateOrder to complete while billing is still WIP
if d.Purchase == nil { if d.Purchase == nil {
return 0, nil return 0, nil
@@ -210,7 +210,7 @@ func (d *PeerItemOrder) GetPrice(request *tools.APIRequest) (float64, error) {
} }
} }
} }
p, err := priced.GetPrice() p, err := priced.GetPriceHT()
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@@ -22,7 +22,7 @@ type Booking struct {
ExecutionMetrics map[string][]models.MetricsSnapshot `json:"metrics,omitempty" bson:"metrics,omitempty"` ExecutionMetrics map[string][]models.MetricsSnapshot `json:"metrics,omitempty" bson:"metrics,omitempty"`
ExecutionsID string `json:"executions_id,omitempty" bson:"executions_id,omitempty" validate:"required"` // ExecutionsID is the ID of the executions ExecutionsID string `json:"executions_id,omitempty" bson:"executions_id,omitempty" validate:"required"` // ExecutionsID is the ID of the executions
DestPeerID string `json:"dest_peer_id,omitempty" bson:"dest_peer_id,omitempty"` // DestPeerID is the ID of the destination peer DestPeerID string `json:"dest_peer_id,omitempty" bson:"dest_peer_id,omitempty"` // DestPeerID is the ID of the destination peer
WorkflowID string `json:"workflow_id,omitempty" bson:"workflow_id,omitempty"` // WorkflowID is the ID of the workflow WorkflowID string `json:"workflow_id,omitempty" bson:"workflow_id,omitempty"` // WorkflowID is the ID of the workflow
ExecutionID string `json:"execution_id,omitempty" bson:"execution_id,omitempty" validate:"required"` ExecutionID string `json:"execution_id,omitempty" bson:"execution_id,omitempty" validate:"required"`
State enum.BookingStatus `json:"state,omitempty" bson:"state,omitempty" validate:"required"` // State is the state of the booking State enum.BookingStatus `json:"state,omitempty" bson:"state,omitempty" validate:"required"` // State is the state of the booking
@@ -118,7 +118,7 @@ func (d *Booking) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor(request) // Create a new instance of the accessor return NewAccessor(request) // Create a new instance of the accessor
} }
func (d *Booking) VerifyAuth(request *tools.APIRequest) bool { func (d *Booking) VerifyAuth(callName string, request *tools.APIRequest) bool {
return true return true
} }

23
models/booking/enums.go Normal file
View File

@@ -0,0 +1,23 @@
package booking
type BookingMode int
const (
PLANNED BookingMode = iota // predictible
PREEMPTED // can be both predictible or unpredictible, first one asking for a quick exec, second on event, but we pay to preempt in any case.
WHEN_POSSIBLE // unpredictable, two mode of payment can be available on that case: fixed, or per USE
)
/*
Ok make a point there:
There is 3 notions about booking & payment :
Booking mode : WHEN is executed
Buying mode : Duration of payment
Pricing Mode : How Many time we pay
We can simplify Buying Mode and Pricing Mode, some Buying Mode implied limited pricing mode
Such as Rules. Just like PERMANENT BUYING can be paid only once.
Booking Mode on WHEN POSSIBLE make an exception, because we can't know when executed.
*/

View File

@@ -42,7 +42,7 @@ func TestBooking_GetAccessor(t *testing.T) {
} }
func TestBooking_VerifyAuth(t *testing.T) { func TestBooking_VerifyAuth(t *testing.T) {
assert.True(t, (&booking.Booking{}).VerifyAuth(nil)) assert.True(t, (&booking.Booking{}).VerifyAuth("get", nil))
} }
func TestBooking_StoreDraftDefault(t *testing.T) { func TestBooking_StoreDraftDefault(t *testing.T) {

View File

@@ -71,7 +71,7 @@ func (ao *CollaborativeArea) Clear(peerID string) {
ao.CollaborativeAreaRule.CreatedAt = time.Now().UTC() ao.CollaborativeAreaRule.CreatedAt = time.Now().UTC()
} }
func (ao *CollaborativeArea) VerifyAuth(request *tools.APIRequest) bool { func (ao *CollaborativeArea) VerifyAuth(callName string, request *tools.APIRequest) bool {
if (ao.AllowedPeersGroup != nil || config.GetConfig().Whitelist) && request != nil { if (ao.AllowedPeersGroup != nil || config.GetConfig().Whitelist) && request != nil {
if grps, ok := ao.AllowedPeersGroup[request.PeerID]; ok || config.GetConfig().Whitelist { if grps, ok := ao.AllowedPeersGroup[request.PeerID]; ok || config.GetConfig().Whitelist {
if slices.Contains(grps, "*") || (!ok && config.GetConfig().Whitelist) { if slices.Contains(grps, "*") || (!ok && config.GetConfig().Whitelist) {
@@ -84,7 +84,7 @@ func (ao *CollaborativeArea) VerifyAuth(request *tools.APIRequest) bool {
} }
} }
} }
return ao.AbstractObject.VerifyAuth(request) return ao.AbstractObject.VerifyAuth(callName, request)
} }
func (d *CollaborativeArea) GetAccessor(request *tools.APIRequest) utils.Accessor { func (d *CollaborativeArea) GetAccessor(request *tools.APIRequest) utils.Accessor {
@@ -97,7 +97,7 @@ func (d *CollaborativeArea) Trim() *CollaborativeArea {
func (d *CollaborativeArea) StoreDraftDefault() { func (d *CollaborativeArea) StoreDraftDefault() {
d.AllowedPeersGroup = map[string][]string{ d.AllowedPeersGroup = map[string][]string{
d.CreatorID: []string{"*"}, d.CreatorID: {"*"},
} }
d.IsDraft = false d.IsDraft = false
} }

View File

@@ -24,6 +24,6 @@ func (d *Rule) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor(request) return NewAccessor(request)
} }
func (d *Rule) VerifyAuth(request *tools.APIRequest) bool { func (d *Rule) VerifyAuth(callName string, request *tools.APIRequest) bool {
return true return true
} }

View File

@@ -3,6 +3,7 @@ package pricing
import ( import (
"time" "time"
"cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
) )
@@ -11,6 +12,9 @@ type PricedItemITF interface {
GetType() tools.DataType GetType() tools.DataType
IsPurchasable() bool IsPurchasable() bool
IsBooked() bool IsBooked() bool
GetQuantity() int
AddQuantity(amount int)
GetBookingMode() booking.BookingMode
GetCreatorID() string GetCreatorID() string
SelectPricing() PricingProfileITF SelectPricing() PricingProfileITF
GetLocationStart() *time.Time GetLocationStart() *time.Time
@@ -18,5 +22,5 @@ type PricedItemITF interface {
SetLocationEnd(end time.Time) SetLocationEnd(end time.Time)
GetLocationEnd() *time.Time GetLocationEnd() *time.Time
GetExplicitDurationInS() float64 GetExplicitDurationInS() float64
GetPrice() (float64, error) GetPriceHT() (float64, error)
} }

View File

@@ -9,7 +9,7 @@ type PricingProfileITF interface {
IsPurchasable() bool IsPurchasable() bool
GetPurchase() BuyingStrategy GetPurchase() BuyingStrategy
GetOverrideStrategyValue() int GetOverrideStrategyValue() int
GetPrice(quantity float64, val float64, start time.Time, end time.Time, params ...string) (float64, error) GetPriceHT(quantity float64, val float64, start time.Time, end time.Time, variation []*PricingVariation, params ...string) (float64, error)
} }
type RefundType int type RefundType int
@@ -34,10 +34,37 @@ type AccessPricingProfile[T Strategy] struct { // only use for acces such as : D
RefundRatio int32 `json:"refund_ratio" bson:"refund_ratio" default:"0"` // RefundRatio is the refund ratio if missing RefundRatio int32 `json:"refund_ratio" bson:"refund_ratio" default:"0"` // RefundRatio is the refund ratio if missing
} }
func (a AccessPricingProfile[T]) IsBooked() bool {
return a.Pricing.BuyingStrategy == SUBSCRIPTION
}
func (a AccessPricingProfile[T]) IsPurchasable() bool {
return a.Pricing.BuyingStrategy == PERMANENT
}
func (a AccessPricingProfile[T]) GetPurchase() BuyingStrategy {
return a.Pricing.BuyingStrategy
}
func (a AccessPricingProfile[T]) GetPriceHT(quantity float64, val float64, start time.Time, end time.Time, variations []*PricingVariation, params ...string) (float64, error) {
return a.Pricing.GetPriceHT(quantity, val, start, &end, variations)
}
func (b *AccessPricingProfile[T]) GetOverrideStrategyValue() int { func (b *AccessPricingProfile[T]) GetOverrideStrategyValue() int {
return -1 return -1
} }
func GetDefaultPricingProfile() PricingProfileITF {
return &AccessPricingProfile[TimePricingStrategy]{
Pricing: PricingStrategy[TimePricingStrategy]{
Price: 0,
Currency: "EUR",
BuyingStrategy: PERMANENT,
TimePricingStrategy: ONCE,
},
}
}
type ExploitPrivilegeStrategy int type ExploitPrivilegeStrategy int
const ( const (

View File

@@ -163,11 +163,37 @@ type PricingStrategy[T Strategy] struct {
OverrideStrategy T `json:"override_strategy" bson:"override_strategy" default:"-1"` // Modulation is the modulation of the pricing OverrideStrategy T `json:"override_strategy" bson:"override_strategy" default:"-1"` // Modulation is the modulation of the pricing
} }
func (p PricingStrategy[T]) GetPrice(amountOfData float64, bookingTimeDuration float64, start time.Time, end *time.Time) (float64, error) { func (p PricingStrategy[T]) GetPriceHT(amountOfData float64, bookingTimeDuration float64, start time.Time, end *time.Time, variations []*PricingVariation) (float64, error) {
if p.BuyingStrategy == SUBSCRIPTION { if p.BuyingStrategy == SUBSCRIPTION {
return BookingEstimation(p.GetTimePricingStrategy(), p.Price*float64(amountOfData), bookingTimeDuration, start, end) price, err := BookingEstimation(p.GetTimePricingStrategy(), p.Price*float64(amountOfData), bookingTimeDuration, start, end)
} else if p.BuyingStrategy == PERMANENT { if err != nil {
return 0, err
}
if variations != nil {
for _, v := range variations {
price = v.GetPriceHT(price)
}
return price, nil
}
return p.Price, nil return p.Price, nil
} else if p.BuyingStrategy == PERMANENT {
if variations != nil {
price := p.Price
for _, v := range variations {
price = v.GetPriceHT(price)
}
return price, nil
}
return p.Price, nil
}
if variations != nil {
price := p.Price
for _, v := range variations {
price = v.GetPriceHT(price)
}
return price, nil
} }
return p.Price * float64(amountOfData), nil return p.Price * float64(amountOfData), nil
} }
@@ -183,3 +209,18 @@ func (p PricingStrategy[T]) GetTimePricingStrategy() TimePricingStrategy {
func (p PricingStrategy[T]) GetOverrideStrategy() T { func (p PricingStrategy[T]) GetOverrideStrategy() T {
return p.OverrideStrategy return p.OverrideStrategy
} }
type PricingVariation struct {
Inflate bool `json:"inflate" bson:"price"` // Price is the Price of the pricing
Percentage float64 `json:"percent" bson:"percent"` // Currency is the currency of the pricing // Modulation is the modulation of the pricing
Priority int `json:"priority" bson:"priority"`
}
func (pv *PricingVariation) GetPriceHT(priceHT float64) float64 {
value := (priceHT * pv.Percentage) / 100
if pv.Inflate {
return priceHT + value
} else {
return priceHT - value
}
}

View File

@@ -100,7 +100,7 @@ func TestPricingStrategy_Getters(t *testing.T) {
assert.Equal(t, DummyStrategy(1), ps.GetOverrideStrategy()) assert.Equal(t, DummyStrategy(1), ps.GetOverrideStrategy())
} }
func TestPricingStrategy_GetPrice(t *testing.T) { func TestPricingStrategy_GetPriceHT(t *testing.T) {
start := time.Now() start := time.Now()
end := start.Add(1 * time.Hour) end := start.Add(1 * time.Hour)
@@ -111,19 +111,19 @@ func TestPricingStrategy_GetPrice(t *testing.T) {
TimePricingStrategy: pricing.PER_HOUR, TimePricingStrategy: pricing.PER_HOUR,
} }
p, err := ps.GetPrice(2, 3600, start, &end) p, err := ps.GetPriceHT(2, 3600, start, &end, nil)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, p > 0) assert.True(t, p > 0)
// UNLIMITED case // UNLIMITED case
ps.BuyingStrategy = pricing.PERMANENT ps.BuyingStrategy = pricing.PERMANENT
p, err = ps.GetPrice(10, 0, start, &end) p, err = ps.GetPriceHT(10, 0, start, &end, nil)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 5.0, p) assert.Equal(t, 5.0, p)
// PAY_PER_USE case // PAY_PER_USE case
//ps.BuyingStrategy = pricing.PAY_PER_USE //ps.BuyingStrategy = pricing.PAY_PER_USE
p, err = ps.GetPrice(3, 0, start, &end) p, err = ps.GetPriceHT(3, 0, start, &end, nil)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 15.0, p) assert.Equal(t, 15.0, p)
} }

View File

@@ -29,6 +29,7 @@ var ModelsCatalog = map[string]func() utils.DBObject{
tools.COMPUTE_RESOURCE.String(): func() utils.DBObject { return &resource.ComputeResource{} }, tools.COMPUTE_RESOURCE.String(): func() utils.DBObject { return &resource.ComputeResource{} },
tools.STORAGE_RESOURCE.String(): func() utils.DBObject { return &resource.StorageResource{} }, tools.STORAGE_RESOURCE.String(): func() utils.DBObject { return &resource.StorageResource{} },
tools.PROCESSING_RESOURCE.String(): func() utils.DBObject { return &resource.ProcessingResource{} }, tools.PROCESSING_RESOURCE.String(): func() utils.DBObject { return &resource.ProcessingResource{} },
tools.NATIVE_TOOL.String(): func() utils.DBObject { return &resource.NativeTool{} },
tools.WORKFLOW.String(): func() utils.DBObject { return &w2.Workflow{} }, tools.WORKFLOW.String(): func() utils.DBObject { return &w2.Workflow{} },
tools.WORKFLOW_EXECUTION.String(): func() utils.DBObject { return &workflow_execution.WorkflowExecution{} }, tools.WORKFLOW_EXECUTION.String(): func() utils.DBObject { return &workflow_execution.WorkflowExecution{} },
tools.WORKSPACE.String(): func() utils.DBObject { return &w3.Workspace{} }, tools.WORKSPACE.String(): func() utils.DBObject { return &w3.Workspace{} },

View File

@@ -26,6 +26,24 @@ func (m PeerState) EnumIndex() int {
return int(m) return int(m)
} }
func GetSelf() (utils.ShallowDBObject, string) {
d, code, err := NewAccessor(nil).Search(nil, SELF.String(), false)
if code != 200 || err != nil || len(d) == 0 {
return nil, ""
}
id := d[0].GetID()
return d[0], id
}
func IsMySelf(peerID string) (bool, string) {
d, code, err := NewAccessor(nil).Search(nil, SELF.String(), false)
if code != 200 || err != nil || len(d) == 0 {
return false, ""
}
id := d[0].GetID()
return peerID == id, id
}
// Peer is a struct that represents a peer // Peer is a struct that represents a peer
type Peer struct { type Peer struct {
utils.AbstractObject utils.AbstractObject
@@ -37,7 +55,7 @@ type Peer struct {
FailedExecution []PeerExecution `json:"failed_execution" bson:"failed_execution"` // FailedExecution is the list of failed executions, to be retried FailedExecution []PeerExecution `json:"failed_execution" bson:"failed_execution"` // FailedExecution is the list of failed executions, to be retried
} }
func (ao *Peer) VerifyAuth(request *tools.APIRequest) bool { func (ao *Peer) VerifyAuth(callName string, request *tools.APIRequest) bool {
return true return true
} }

View File

@@ -31,15 +31,18 @@ func (r *ComputeResource) GetType() string {
return tools.COMPUTE_RESOURCE.String() return tools.COMPUTE_RESOURCE.String()
} }
func (abs *ComputeResource) ConvertToPricedResource(t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { func (abs *ComputeResource) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
if t != tools.COMPUTE_RESOURCE { if t != tools.COMPUTE_RESOURCE {
return nil return nil, errors.New("not the proper type expected : cannot convert to priced resource : have " + t.String() + " wait Compute")
}
p, err := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, request)
if err != nil {
return nil, err
} }
p := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, request)
priced := p.(*PricedResource) priced := p.(*PricedResource)
return &PricedComputeResource{ return &PricedComputeResource{
PricedResource: *priced, PricedResource: *priced,
} }, nil
} }
type ComputeNode struct { type ComputeNode struct {
@@ -68,32 +71,6 @@ func NewComputeResourceInstance(name string, peerID string) ResourceInstanceITF
UUID: uuid.New().String(), UUID: uuid.New().String(),
Name: name, Name: name,
}, },
Partnerships: []*ComputeResourcePartnership{
{
ResourcePartnerShip: ResourcePartnerShip[*ComputeResourcePricingProfile]{
Namespace: "default",
PeerGroups: map[string][]string{
peerID: {"*"},
},
PricingProfiles: map[int]map[int]*ComputeResourcePricingProfile{
0: {
0: &ComputeResourcePricingProfile{
ExploitPricingProfile: pricing.ExploitPricingProfile[pricing.TimePricingStrategy]{
AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{
Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{
Price: 0,
Currency: "EUR",
TimePricingStrategy: pricing.ONCE,
BuyingStrategy: pricing.PERMANENT,
},
},
},
},
},
},
},
},
},
}, },
} }
} }
@@ -138,7 +115,7 @@ func (p *ComputeResourcePricingProfile) GetOverrideStrategyValue() int {
// NOT A PROPER QUANTITY // NOT A PROPER QUANTITY
// amountOfData is the number of CPUs, GPUs or RAM dependings on the params // amountOfData is the number of CPUs, GPUs or RAM dependings on the params
func (p *ComputeResourcePricingProfile) GetPrice(amountOfData float64, explicitDuration float64, start time.Time, end time.Time, params ...string) (float64, error) { func (p *ComputeResourcePricingProfile) GetPriceHT(amountOfData float64, explicitDuration float64, start time.Time, end time.Time, variation []*pricing.PricingVariation, params ...string) (float64, error) {
if len(params) < 1 { if len(params) < 1 {
return 0, errors.New("params must be set") return 0, errors.New("params must be set")
} }
@@ -148,7 +125,7 @@ func (p *ComputeResourcePricingProfile) GetPrice(amountOfData float64, explicitD
if _, ok := p.CPUsPrices[model]; ok { if _, ok := p.CPUsPrices[model]; ok {
p.Pricing.Price = p.CPUsPrices[model] p.Pricing.Price = p.CPUsPrices[model]
} }
r, err := p.Pricing.GetPrice(amountOfData, explicitDuration, start, &end) r, err := p.Pricing.GetPriceHT(amountOfData, explicitDuration, start, &end, variation)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -159,7 +136,7 @@ func (p *ComputeResourcePricingProfile) GetPrice(amountOfData float64, explicitD
if _, ok := p.GPUsPrices[model]; ok { if _, ok := p.GPUsPrices[model]; ok {
p.Pricing.Price = p.GPUsPrices[model] p.Pricing.Price = p.GPUsPrices[model]
} }
r, err := p.Pricing.GetPrice(amountOfData, explicitDuration, start, &end) r, err := p.Pricing.GetPriceHT(amountOfData, explicitDuration, start, &end, variation)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -169,7 +146,7 @@ func (p *ComputeResourcePricingProfile) GetPrice(amountOfData float64, explicitD
if p.RAMPrice >= 0 { if p.RAMPrice >= 0 {
p.Pricing.Price = p.RAMPrice p.Pricing.Price = p.RAMPrice
} }
r, err := p.Pricing.GetPrice(float64(amountOfData), explicitDuration, start, &end) r, err := p.Pricing.GetPriceHT(float64(amountOfData), explicitDuration, start, &end, variation)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -190,14 +167,17 @@ func (r *PricedComputeResource) GetType() tools.DataType {
return tools.COMPUTE_RESOURCE return tools.COMPUTE_RESOURCE
} }
func (r *PricedComputeResource) GetPrice() (float64, error) { func (r *PricedComputeResource) GetPriceHT() (float64, error) {
now := time.Now() if r.BookingConfiguration == nil {
if r.UsageStart == nil { r.BookingConfiguration = &BookingConfiguration{}
r.UsageStart = &now
} }
if r.UsageEnd == nil { now := time.Now()
add := r.UsageStart.Add(time.Duration(1 * time.Hour)) if r.BookingConfiguration.UsageStart == nil {
r.UsageEnd = &add r.BookingConfiguration.UsageStart = &now
}
if r.BookingConfiguration.UsageEnd == nil {
add := r.BookingConfiguration.UsageStart.Add(time.Duration(1 * time.Hour))
r.BookingConfiguration.UsageEnd = &add
} }
if r.SelectedPricing == nil { if r.SelectedPricing == nil {
return 0, errors.New("pricing profile must be set on Priced Compute" + r.ResourceID) return 0, errors.New("pricing profile must be set on Priced Compute" + r.ResourceID)
@@ -206,14 +186,17 @@ func (r *PricedComputeResource) GetPrice() (float64, error) {
price := float64(0) price := float64(0)
for _, l := range []map[string]float64{r.CPUsLocated, r.GPUsLocated} { for _, l := range []map[string]float64{r.CPUsLocated, r.GPUsLocated} {
for model, amountOfData := range l { for model, amountOfData := range l {
cpus, err := pricing.GetPrice(float64(amountOfData), r.ExplicitBookingDurationS, *r.UsageStart, *r.UsageEnd, "cpus", model) cpus, err := pricing.GetPriceHT(float64(amountOfData),
r.BookingConfiguration.ExplicitBookingDurationS, *r.BookingConfiguration.UsageStart,
*r.BookingConfiguration.UsageEnd, r.Variations, "cpus", model)
if err != nil { if err != nil {
return 0, err return 0, err
} }
price += cpus price += cpus
} }
} }
ram, err := pricing.GetPrice(r.RAMLocated, r.ExplicitBookingDurationS, *r.UsageStart, *r.UsageEnd, "ram") ram, err := pricing.GetPriceHT(r.RAMLocated, r.BookingConfiguration.ExplicitBookingDurationS,
*r.BookingConfiguration.UsageStart, *r.BookingConfiguration.UsageEnd, r.Variations, "ram")
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@@ -38,15 +38,18 @@ func (r *DataResource) GetType() string {
return tools.DATA_RESOURCE.String() return tools.DATA_RESOURCE.String()
} }
func (abs *DataResource) ConvertToPricedResource(t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { func (abs *DataResource) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
if t != tools.DATA_RESOURCE { if t != tools.DATA_RESOURCE {
return nil return nil, errors.New("not the proper type expected : cannot convert to priced resource : have " + t.String() + " wait Data")
}
p, err := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, request)
if err != nil {
return nil, err
} }
p := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, request)
priced := p.(*PricedResource) priced := p.(*PricedResource)
return &PricedDataResource{ return &PricedDataResource{
PricedResource: *priced, PricedResource: *priced,
} }, nil
} }
type DataInstance struct { type DataInstance struct {
@@ -61,30 +64,6 @@ func NewDataInstance(name string, peerID string) ResourceInstanceITF {
UUID: uuid.New().String(), UUID: uuid.New().String(),
Name: name, Name: name,
}, },
Partnerships: []*DataResourcePartnership{
{
ResourcePartnerShip: ResourcePartnerShip[*DataResourcePricingProfile]{
Namespace: "default",
PeerGroups: map[string][]string{
peerID: {"*"},
},
PricingProfiles: map[int]map[int]*DataResourcePricingProfile{
0: {
0: &DataResourcePricingProfile{
AccessPricingProfile: pricing.AccessPricingProfile[DataResourcePricingStrategy]{
Pricing: pricing.PricingStrategy[DataResourcePricingStrategy]{
Price: 0,
Currency: "EUR",
TimePricingStrategy: pricing.ONCE,
BuyingStrategy: pricing.PERMANENT,
},
},
},
},
},
},
},
},
}, },
} }
} }
@@ -172,14 +151,6 @@ func (p *DataResourcePricingProfile) GetOverrideStrategyValue() int {
return p.Pricing.OverrideStrategy.GetStrategyValue() return p.Pricing.OverrideStrategy.GetStrategyValue()
} }
func (p *DataResourcePricingProfile) GetPrice(amountOfData float64, explicitDuration float64, start time.Time, end time.Time, params ...string) (float64, error) {
return p.Pricing.GetPrice(amountOfData, explicitDuration, start, &end)
}
func (p *DataResourcePricingProfile) GetPurchase() pricing.BuyingStrategy {
return p.Pricing.BuyingStrategy
}
func (p *DataResourcePricingProfile) IsPurchasable() bool { func (p *DataResourcePricingProfile) IsPurchasable() bool {
return p.Pricing.BuyingStrategy != pricing.UNDEFINED_SUBSCRIPTION return p.Pricing.BuyingStrategy != pricing.UNDEFINED_SUBSCRIPTION
} }
@@ -198,15 +169,18 @@ func (r *PricedDataResource) GetType() tools.DataType {
return tools.DATA_RESOURCE return tools.DATA_RESOURCE
} }
func (r *PricedDataResource) GetPrice() (float64, error) { func (r *PricedDataResource) GetPriceHT() (float64, error) {
fmt.Println("GetPrice", r.UsageStart, r.UsageEnd) if r.BookingConfiguration == nil {
now := time.Now() r.BookingConfiguration = &BookingConfiguration{}
if r.UsageStart == nil {
r.UsageStart = &now
} }
if r.UsageEnd == nil { fmt.Println("GetPriceHT", r.BookingConfiguration.UsageStart, r.BookingConfiguration.UsageEnd)
add := r.UsageStart.Add(time.Duration(1 * time.Hour)) now := time.Now()
r.UsageEnd = &add if r.BookingConfiguration.UsageStart == nil {
r.BookingConfiguration.UsageStart = &now
}
if r.BookingConfiguration.UsageEnd == nil {
add := r.BookingConfiguration.UsageStart.Add(time.Duration(1 * time.Hour))
r.BookingConfiguration.UsageEnd = &add
} }
if r.SelectedPricing == nil { if r.SelectedPricing == nil {
return 0, errors.New("pricing profile must be set on Priced Data" + r.ResourceID) return 0, errors.New("pricing profile must be set on Priced Data" + r.ResourceID)
@@ -220,5 +194,7 @@ func (r *PricedDataResource) GetPrice() (float64, error) {
return 0, err return 0, err
} }
} }
return pricing.GetPrice(amountOfData, r.ExplicitBookingDurationS, *r.UsageStart, *r.UsageEnd)
return pricing.GetPriceHT(amountOfData, r.BookingConfiguration.ExplicitBookingDurationS,
*r.BookingConfiguration.UsageStart, *r.BookingConfiguration.UsageEnd, r.Variations)
} }

View File

@@ -1,6 +1,7 @@
package resources package resources
import ( import (
"cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
@@ -9,9 +10,10 @@ import (
type ResourceInterface interface { type ResourceInterface interface {
utils.DBObject utils.DBObject
Trim() Trim()
ConvertToPricedResource(t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF GetBookingModes() map[booking.BookingMode]*pricing.PricingVariation
ConvertToPricedResource(t tools.DataType, a *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, b *int, request *tools.APIRequest) (pricing.PricedItemITF, error)
GetType() string GetType() string
GetSelectedInstance() ResourceInstanceITF GetSelectedInstance(selected *int) ResourceInstanceITF
ClearEnv() utils.DBObject ClearEnv() utils.DBObject
SetAllowedInstances(request *tools.APIRequest) SetAllowedInstances(request *tools.APIRequest)
AddInstances(instance ResourceInstanceITF) AddInstances(instance ResourceInstanceITF)
@@ -23,17 +25,15 @@ type ResourceInstanceITF interface {
GetName() string GetName() string
StoreDraftDefault() StoreDraftDefault()
ClearEnv() ClearEnv()
GetProfile() pricing.PricingProfileITF GetProfile(peerID string, partnershipIndex *int, buying *int, strategy *int) pricing.PricingProfileITF
GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF
GetPeerGroups() ([]ResourcePartnerITF, []map[string][]string) GetPeerGroups() ([]ResourcePartnerITF, []map[string][]string)
ClearPeerGroups() ClearPeerGroups()
GetSelectedPartnership(peerID string, groups []string) ResourcePartnerITF
GetPartnerships(peerID string, groups []string) []ResourcePartnerITF
} }
type ResourcePartnerITF interface { type ResourcePartnerITF interface {
GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF
GetPeerGroups() map[string][]string GetPeerGroups() map[string][]string
ClearPeerGroups() ClearPeerGroups()
GetProfile(buying int, strategy int) pricing.PricingProfileITF GetProfile(buying *int, strategy *int) pricing.PricingProfileITF
} }

View File

@@ -11,12 +11,14 @@ type ResourceSet struct {
Processings []string `bson:"processings,omitempty" json:"processings,omitempty"` Processings []string `bson:"processings,omitempty" json:"processings,omitempty"`
Computes []string `bson:"computes,omitempty" json:"computes,omitempty"` Computes []string `bson:"computes,omitempty" json:"computes,omitempty"`
Workflows []string `bson:"workflows,omitempty" json:"workflows,omitempty"` Workflows []string `bson:"workflows,omitempty" json:"workflows,omitempty"`
NativeTool []string `bson:"native,omitempty" json:"native,omitempty"`
DataResources []*DataResource `bson:"-" json:"data_resources,omitempty"` DataResources []*DataResource `bson:"-" json:"data_resources,omitempty"`
StorageResources []*StorageResource `bson:"-" json:"storage_resources,omitempty"` StorageResources []*StorageResource `bson:"-" json:"storage_resources,omitempty"`
ProcessingResources []*ProcessingResource `bson:"-" json:"processing_resources,omitempty"` ProcessingResources []*ProcessingResource `bson:"-" json:"processing_resources,omitempty"`
ComputeResources []*ComputeResource `bson:"-" json:"compute_resources,omitempty"` ComputeResources []*ComputeResource `bson:"-" json:"compute_resources,omitempty"`
WorkflowResources []*WorkflowResource `bson:"-" json:"workflow_resources,omitempty"` WorkflowResources []*WorkflowResource `bson:"-" json:"workflow_resources,omitempty"`
NativeTools []*NativeTool `bson:"-" json:"native_tools,omitempty"`
} }
func (r *ResourceSet) Clear() { func (r *ResourceSet) Clear() {
@@ -62,4 +64,5 @@ type ItemResource struct {
Storage *StorageResource `bson:"storage,omitempty" json:"storage,omitempty"` Storage *StorageResource `bson:"storage,omitempty" json:"storage,omitempty"`
Compute *ComputeResource `bson:"compute,omitempty" json:"compute,omitempty"` Compute *ComputeResource `bson:"compute,omitempty" json:"compute,omitempty"`
Workflow *WorkflowResource `bson:"workflow,omitempty" json:"workflow,omitempty"` Workflow *WorkflowResource `bson:"workflow,omitempty" json:"workflow,omitempty"`
NativeTool *NativeTool `bson:"native_tools,omitempty" json:"native_tools,omitempty"`
} }

View File

@@ -0,0 +1,73 @@
package resources
import (
"encoding/json"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/resources/native_tools"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
)
/*
* NativeT ools is a struct that represents Native Functionnality of OPENCLOUD
*/
type NativeTool struct {
AbstractResource
Kind int `json:"kind" bson:"kind" validate:"required"`
Params map[string]interface{}
}
func (d *NativeTool) SetName(name string) {
d.Name = name
}
func (d *NativeTool) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor[*NativeTool](tools.NATIVE_TOOL, request, func() utils.DBObject { return &NativeTool{} })
}
func (r *NativeTool) AddInstances(instance ResourceInstanceITF) {
}
func (r *NativeTool) GetType() string {
return tools.NATIVE_TOOL.String()
}
func (d *NativeTool) ClearEnv() utils.DBObject {
return d
}
func (d *NativeTool) Trim() {
/* EMPTY */
}
func (w *NativeTool) SetAllowedInstances(request *tools.APIRequest) {
/* EMPTY */
}
func (w *NativeTool) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
return &PricedResource{
Name: w.Name,
Logo: w.Logo,
ResourceID: w.UUID,
ResourceType: t,
Quantity: 1,
CreatorID: w.CreatorID,
}, nil
}
func InitNative() {
for _, kind := range []native_tools.NativeToolsEnum{native_tools.WORKFLOW_EVENT} {
newNative := &NativeTool{}
access := newNative.GetAccessor(&tools.APIRequest{Admin: true})
l, _, err := access.Search(nil, kind.String(), false)
if err != nil || len(l) == 0 {
newNative.Name = kind.String()
newNative.Kind = int(kind)
b, _ := json.Marshal(kind.Params())
var m map[string]interface{}
json.Unmarshal(b, &m)
newNative.Params = m
access.StoreOne(newNative)
}
}
}

View File

@@ -0,0 +1,23 @@
package native_tools
type NativeToolsEnum int
const (
WORKFLOW_EVENT NativeToolsEnum = iota
)
var Params = [...]interface{}{
WorkflowEventParams{},
}
var Str = [...]string{
"WORKFLOW_EVENT",
}
func (d NativeToolsEnum) Params() interface{} {
return Str[d]
}
func (d NativeToolsEnum) String() string {
return Str[d]
}

View File

@@ -0,0 +1,19 @@
package native_tools
import (
"cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
)
/*
* Workflow Event is a struct that represents a native functiunality.
*/
type WorkflowEventParams struct {
WorkflowResourceID string `json:"workflow_execution_id" bson:"workflow_execution_id" validate:"required"`
BookingMode *booking.BookingMode `json:"booking_mode" bson:"booking_mode"`
}
func (wep *WorkflowEventParams) GetBuyingStrategy() pricing.BillingStrategy {
return pricing.BILL_ONCE
}

View File

@@ -5,21 +5,37 @@ import (
"fmt" "fmt"
"time" "time"
"cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
) )
type BookingConfiguration struct {
ExplicitBookingDurationS float64 `json:"explicit_location_duration_s,omitempty" bson:"explicit_location_duration_s,omitempty"`
UsageStart *time.Time `json:"start,omitempty" bson:"start,omitempty"`
UsageEnd *time.Time `json:"end,omitempty" bson:"end,omitempty"`
Mode booking.BookingMode `json:"mode,omitempty" bson:"mode,omitempty"`
}
type PricedResource struct { type PricedResource struct {
Name string `json:"name,omitempty" bson:"name,omitempty"` Name string `json:"name,omitempty" bson:"name,omitempty"`
Logo string `json:"logo,omitempty" bson:"logo,omitempty"` Logo string `json:"logo,omitempty" bson:"logo,omitempty"`
InstancesRefs map[string]string `json:"instances_refs,omitempty" bson:"instances_refs,omitempty"` InstancesRefs map[string]string `json:"instances_refs,omitempty" bson:"instances_refs,omitempty"`
SelectedPricing pricing.PricingProfileITF `json:"selected_pricing,omitempty" bson:"selected_pricing,omitempty"` SelectedPricing pricing.PricingProfileITF `json:"selected_pricing,omitempty" bson:"selected_pricing,omitempty"`
ExplicitBookingDurationS float64 `json:"explicit_location_duration_s,omitempty" bson:"explicit_location_duration_s,omitempty"` Quantity int `json:"quantity,omitempty" bson:"quantity,omitempty"`
UsageStart *time.Time `json:"start,omitempty" bson:"start,omitempty"` BookingConfiguration *BookingConfiguration `json:"booking_configuration,omitempty" bson:"booking_configuration,omitempty"`
UsageEnd *time.Time `json:"end,omitempty" bson:"end,omitempty"` Variations []*pricing.PricingVariation `json:"pricing_variations" bson:"pricing_variations"`
CreatorID string `json:"peer_id,omitempty" bson:"peer_id,omitempty"` CreatorID string `json:"peer_id,omitempty" bson:"peer_id,omitempty"`
ResourceID string `json:"resource_id,omitempty" bson:"resource_id,omitempty"` ResourceID string `json:"resource_id,omitempty" bson:"resource_id,omitempty"`
ResourceType tools.DataType `json:"resource_type,omitempty" bson:"resource_type,omitempty"` ResourceType tools.DataType `json:"resource_type,omitempty" bson:"resource_type,omitempty"`
}
func (abs *PricedResource) GetQuantity() int {
return abs.Quantity
}
func (abs *PricedResource) AddQuantity(amount int) {
abs.Quantity += amount
} }
func (abs *PricedResource) SelectPricing() pricing.PricingProfileITF { func (abs *PricedResource) SelectPricing() pricing.PricingProfileITF {
@@ -46,7 +62,7 @@ func (abs *PricedResource) IsPurchasable() bool {
} }
func (abs *PricedResource) IsBooked() bool { func (abs *PricedResource) IsBooked() bool {
return true // For dev purposes, prevent that DB objects that don't have a Pricing are considered as not booked return true // For dev purposes, prevent that DB objects that don't have a Pricing are considered as not booked
if abs.SelectedPricing == nil { if abs.SelectedPricing == nil {
return false return false
} }
@@ -54,48 +70,73 @@ func (abs *PricedResource) IsBooked() bool {
} }
func (abs *PricedResource) GetLocationEnd() *time.Time { func (abs *PricedResource) GetLocationEnd() *time.Time {
return abs.UsageEnd if abs.BookingConfiguration == nil {
return nil
}
return abs.BookingConfiguration.UsageEnd
} }
func (abs *PricedResource) GetLocationStart() *time.Time { func (abs *PricedResource) GetLocationStart() *time.Time {
return abs.UsageStart if abs.BookingConfiguration == nil {
return nil
}
return abs.BookingConfiguration.UsageStart
} }
func (abs *PricedResource) SetLocationStart(start time.Time) { func (abs *PricedResource) SetLocationStart(start time.Time) {
abs.UsageStart = &start if abs.BookingConfiguration == nil {
abs.BookingConfiguration = &BookingConfiguration{}
}
abs.BookingConfiguration.UsageStart = &start
} }
func (abs *PricedResource) SetLocationEnd(end time.Time) { func (abs *PricedResource) SetLocationEnd(end time.Time) {
abs.UsageEnd = &end if abs.BookingConfiguration == nil {
abs.BookingConfiguration = &BookingConfiguration{}
}
abs.BookingConfiguration.UsageEnd = &end
}
func (abs *PricedResource) GetBookingMode() booking.BookingMode {
if abs.BookingConfiguration == nil {
return booking.WHEN_POSSIBLE
}
return abs.BookingConfiguration.Mode
} }
func (abs *PricedResource) GetExplicitDurationInS() float64 { func (abs *PricedResource) GetExplicitDurationInS() float64 {
if abs.ExplicitBookingDurationS == 0 { if abs.BookingConfiguration == nil {
if abs.UsageEnd == nil && abs.UsageStart == nil { abs.BookingConfiguration = &BookingConfiguration{}
}
if abs.BookingConfiguration.ExplicitBookingDurationS == 0 {
if abs.BookingConfiguration.UsageEnd == nil && abs.BookingConfiguration.UsageStart == nil {
return time.Duration(1 * time.Hour).Seconds() return time.Duration(1 * time.Hour).Seconds()
} }
if abs.UsageEnd == nil { if abs.BookingConfiguration.UsageEnd == nil {
add := abs.UsageStart.Add(time.Duration(1 * time.Hour)) add := abs.BookingConfiguration.UsageStart.Add(time.Duration(1 * time.Hour))
abs.UsageEnd = &add abs.BookingConfiguration.UsageEnd = &add
} }
return abs.UsageEnd.Sub(*abs.UsageStart).Seconds() return abs.BookingConfiguration.UsageEnd.Sub(*abs.BookingConfiguration.UsageStart).Seconds()
} }
return abs.ExplicitBookingDurationS return abs.BookingConfiguration.ExplicitBookingDurationS
} }
func (r *PricedResource) GetPrice() (float64, error) { func (r *PricedResource) GetPriceHT() (float64, error) {
fmt.Println("GetPrice", r.UsageStart, r.UsageEnd)
now := time.Now() now := time.Now()
if r.UsageStart == nil { if r.BookingConfiguration == nil {
r.UsageStart = &now r.BookingConfiguration = &BookingConfiguration{}
} }
if r.UsageEnd == nil { fmt.Println("GetPriceHT", r.BookingConfiguration.UsageStart, r.BookingConfiguration.UsageEnd)
add := r.UsageStart.Add(time.Duration(1 * time.Hour)) if r.BookingConfiguration.UsageStart == nil {
r.UsageEnd = &add r.BookingConfiguration.UsageStart = &now
}
if r.BookingConfiguration.UsageEnd == nil {
add := r.BookingConfiguration.UsageStart.Add(time.Duration(1 * time.Hour))
r.BookingConfiguration.UsageEnd = &add
} }
if r.SelectedPricing == nil { if r.SelectedPricing == nil {
return 0, errors.New("pricing profile must be set on Priced Resource " + r.ResourceID) return 0, errors.New("pricing profile must be set on Priced Resource " + r.ResourceID)
} }
pricing := r.SelectedPricing pricing := r.SelectedPricing
return pricing.GetPrice(1, 0, *r.UsageStart, *r.UsageEnd) return pricing.GetPriceHT(1, 0, *r.BookingConfiguration.UsageStart, *r.BookingConfiguration.UsageEnd, r.Variations)
} }

View File

@@ -56,28 +56,6 @@ func NewProcessingInstance(name string, peerID string) ResourceInstanceITF {
UUID: uuid.New().String(), UUID: uuid.New().String(),
Name: name, Name: name,
}, },
Partnerships: []*ResourcePartnerShip[*ProcessingResourcePricingProfile]{
{
Namespace: "default",
PeerGroups: map[string][]string{
peerID: {"*"},
},
PricingProfiles: map[int]map[int]*ProcessingResourcePricingProfile{
0: {
0: &ProcessingResourcePricingProfile{
AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{
Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{
Price: 0,
Currency: "EUR",
TimePricingStrategy: pricing.ONCE,
BuyingStrategy: pricing.PERMANENT,
},
},
},
},
},
},
},
}, },
} }
} }
@@ -92,16 +70,19 @@ func (r *PricedProcessingResource) GetType() tools.DataType {
} }
func (a *PricedProcessingResource) GetExplicitDurationInS() float64 { func (a *PricedProcessingResource) GetExplicitDurationInS() float64 {
if a.ExplicitBookingDurationS == 0 { if a.BookingConfiguration == nil {
if a.IsService || a.UsageStart == nil { a.BookingConfiguration = &BookingConfiguration{}
}
if a.BookingConfiguration.ExplicitBookingDurationS == 0 {
if a.IsService || a.BookingConfiguration.UsageStart == nil {
if a.IsService { if a.IsService {
return -1 return -1
} }
return time.Duration(1 * time.Hour).Seconds() return time.Duration(1 * time.Hour).Seconds()
} }
return a.UsageEnd.Sub(*a.UsageStart).Seconds() return a.BookingConfiguration.UsageEnd.Sub(*a.BookingConfiguration.UsageStart).Seconds()
} }
return a.ExplicitBookingDurationS return a.BookingConfiguration.ExplicitBookingDurationS
} }
func (d *ProcessingResource) GetAccessor(request *tools.APIRequest) utils.Accessor { func (d *ProcessingResource) GetAccessor(request *tools.APIRequest) utils.Accessor {
@@ -119,11 +100,3 @@ func (p *ProcessingResourcePricingProfile) IsPurchasable() bool {
func (p *ProcessingResourcePricingProfile) IsBooked() bool { func (p *ProcessingResourcePricingProfile) IsBooked() bool {
return p.Pricing.BuyingStrategy != pricing.PERMANENT return p.Pricing.BuyingStrategy != pricing.PERMANENT
} }
func (p *ProcessingResourcePricingProfile) GetPurchase() pricing.BuyingStrategy {
return p.Pricing.BuyingStrategy
}
func (p *ProcessingResourcePricingProfile) GetPrice(amountOfData float64, val float64, start time.Time, end time.Time, params ...string) (float64, error) {
return p.Pricing.GetPrice(amountOfData, val, start, &end)
}

View File

@@ -1,9 +1,11 @@
package resources package resources
import ( import (
"errors"
"slices" "slices"
"cloud.o-forge.io/core/oc-lib/config" "cloud.o-forge.io/core/oc-lib/config"
"cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/common/models" "cloud.o-forge.io/core/oc-lib/models/common/models"
"cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/peer"
@@ -15,17 +17,30 @@ import (
// AbstractResource is the struct containing all of the attributes commons to all ressources // AbstractResource is the struct containing all of the attributes commons to all ressources
type AbstractResource struct { type AbstractResource struct {
utils.AbstractObject // AbstractObject contains the basic fields of an object (id, name) utils.AbstractObject // AbstractObject contains the basic fields of an object (id, name)
Type string `json:"type,omitempty" bson:"type,omitempty"` // Type is the type of the resource Type string `json:"type,omitempty" bson:"type,omitempty"` // Type is the type of the resource
Logo string `json:"logo,omitempty" bson:"logo,omitempty" validate:"required"` // Logo is the logo of the resource Logo string `json:"logo,omitempty" bson:"logo,omitempty" validate:"required"` // Logo is the logo of the resource
Description string `json:"description,omitempty" bson:"description,omitempty"` // Description is the description of the resource Description string `json:"description,omitempty" bson:"description,omitempty"` // Description is the description of the resource
ShortDescription string `json:"short_description,omitempty" bson:"short_description,omitempty" validate:"required"` // ShortDescription is the short description of the resource ShortDescription string `json:"short_description,omitempty" bson:"short_description,omitempty" validate:"required"` // ShortDescription is the short description of the resource
Owners []utils.Owner `json:"owners,omitempty" bson:"owners,omitempty"` // Owners is the list of owners of the resource Owners []utils.Owner `json:"owners,omitempty" bson:"owners,omitempty"` // Owners is the list of owners of the resource
UsageRestrictions string `bson:"usage_restrictions,omitempty" json:"usage_restrictions,omitempty"` UsageRestrictions string `bson:"usage_restrictions,omitempty" json:"usage_restrictions,omitempty"`
SelectedInstanceIndex *int `json:"selected_instance_index,omitempty" bson:"selected_instance_index,omitempty"` // SelectedInstance is the selected instance AllowedBookingModes map[booking.BookingMode]*pricing.PricingVariation `bson:"allowed_booking_modes" json:"allowed_booking_modes"`
} }
func (r *AbstractResource) GetSelectedInstance() ResourceInstanceITF { func (r *AbstractResource) GetBookingModes() map[booking.BookingMode]*pricing.PricingVariation {
if len(r.AllowedBookingModes) == 0 {
return map[booking.BookingMode]*pricing.PricingVariation{
booking.PLANNED: {
Percentage: 0,
}, booking.WHEN_POSSIBLE: {
Percentage: 0,
},
}
}
return r.AllowedBookingModes
}
func (r *AbstractResource) GetSelectedInstance(selected *int) ResourceInstanceITF {
return nil return nil
} }
@@ -57,29 +72,43 @@ func (abs *AbstractInstanciatedResource[T]) AddInstances(instance ResourceInstan
abs.Instances = append(abs.Instances, instance.(T)) abs.Instances = append(abs.Instances, instance.(T))
} }
func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource(t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
instances := map[string]string{} instances := map[string]string{}
profiles := []pricing.PricingProfileITF{} profiles := []pricing.PricingProfileITF{}
for _, instance := range abs.Instances { for _, instance := range abs.Instances { // TODO why it crush before ?
instances[instance.GetID()] = instance.GetName() instances[instance.GetID()] = instance.GetName()
profiles = instance.GetPricingsProfiles(request.PeerID, request.Groups) profiles = instance.GetPricingsProfiles(request.PeerID, request.Groups)
} }
var profile pricing.PricingProfileITF var profile pricing.PricingProfileITF
if t := abs.GetSelectedInstance(); t != nil { if t := abs.GetSelectedInstance(selectedInstance); t != nil {
profile = t.GetProfile() profile = t.GetProfile(request.PeerID, selectedPartnership, selectedBuyingStrategy, selectedStrategy)
} }
if profile == nil && len(profiles) > 0 { if profile == nil {
profile = profiles[0] if len(profiles) > 0 {
profile = profiles[0]
} else {
if ok, _ := peer.IsMySelf(request.PeerID); ok {
profile = pricing.GetDefaultPricingProfile()
} else {
return nil, errors.New("no pricing profile found")
}
}
}
variations := []*pricing.PricingVariation{}
if selectedBookingModeIndex != nil && abs.AllowedBookingModes[booking.BookingMode(*selectedBookingModeIndex)] != nil {
variations = append(variations, abs.AllowedBookingModes[booking.BookingMode(*selectedBookingModeIndex)])
} }
return &PricedResource{ return &PricedResource{
Name: abs.Name, Name: abs.Name,
Logo: abs.Logo, Logo: abs.Logo,
ResourceID: abs.UUID, ResourceID: abs.UUID,
ResourceType: t, ResourceType: t,
Quantity: 1,
InstancesRefs: instances, InstancesRefs: instances,
SelectedPricing: profile, SelectedPricing: profile,
Variations: variations,
CreatorID: abs.CreatorID, CreatorID: abs.CreatorID,
} }, nil
} }
func (abs *AbstractInstanciatedResource[T]) ClearEnv() utils.DBObject { func (abs *AbstractInstanciatedResource[T]) ClearEnv() utils.DBObject {
@@ -89,9 +118,9 @@ func (abs *AbstractInstanciatedResource[T]) ClearEnv() utils.DBObject {
return abs return abs
} }
func (r *AbstractInstanciatedResource[T]) GetSelectedInstance() ResourceInstanceITF { func (r *AbstractInstanciatedResource[T]) GetSelectedInstance(selected *int) ResourceInstanceITF {
if r.SelectedInstanceIndex != nil && len(r.Instances) > *r.SelectedInstanceIndex { if selected != nil && len(r.Instances) > *selected {
return r.Instances[*r.SelectedInstanceIndex] return r.Instances[*selected]
} }
if len(r.Instances) > 0 { if len(r.Instances) > 0 {
return r.Instances[0] return r.Instances[0]
@@ -115,8 +144,8 @@ func (d *AbstractInstanciatedResource[T]) Trim() {
} }
} }
func (abs *AbstractInstanciatedResource[T]) VerifyAuth(request *tools.APIRequest) bool { func (abs *AbstractInstanciatedResource[T]) VerifyAuth(callName string, request *tools.APIRequest) bool {
return len(VerifyAuthAction[T](abs.Instances, request)) > 0 || abs.AbstractObject.VerifyAuth(request) return len(VerifyAuthAction[T](abs.Instances, request)) > 0 || abs.AbstractObject.VerifyAuth(callName, request)
} }
func VerifyAuthAction[T ResourceInstanceITF](baseInstance []T, request *tools.APIRequest) []T { func VerifyAuthAction[T ResourceInstanceITF](baseInstance []T, request *tools.APIRequest) []T {
@@ -150,18 +179,22 @@ type GeoPoint struct {
type ResourceInstance[T ResourcePartnerITF] struct { type ResourceInstance[T ResourcePartnerITF] struct {
utils.AbstractObject utils.AbstractObject
Location GeoPoint `json:"location,omitempty" bson:"location,omitempty"` Location GeoPoint `json:"location,omitempty" bson:"location,omitempty"`
Country countries.CountryCode `json:"country,omitempty" bson:"country,omitempty"` Country countries.CountryCode `json:"country,omitempty" bson:"country,omitempty"`
AccessProtocol string `json:"access_protocol,omitempty" bson:"access_protocol,omitempty"` AccessProtocol string `json:"access_protocol,omitempty" bson:"access_protocol,omitempty"`
Env []models.Param `json:"env,omitempty" bson:"env,omitempty"` Env []models.Param `json:"env,omitempty" bson:"env,omitempty"`
Inputs []models.Param `json:"inputs,omitempty" bson:"inputs,omitempty"` Inputs []models.Param `json:"inputs,omitempty" bson:"inputs,omitempty"`
Outputs []models.Param `json:"outputs,omitempty" bson:"outputs,omitempty"` Outputs []models.Param `json:"outputs,omitempty" bson:"outputs,omitempty"`
SelectedPartnershipIndex int `json:"selected_partnership_index,omitempty" bson:"selected_partnership_index,omitempty"`
SelectedBuyingStrategy int `json:"selected_buying_strategy,omitempty" bson:"selected_buying_strategy,omitempty"` // SelectedPartnershipIndex int `json:"selected_partnership_index,omitempty" bson:"selected_partnership_index,omitempty"`
SelectedStrategy int `json:"selected_strategy,omitempty" bson:"selected_strategy,omitempty"` // SelectedBuyingStrategy int `json:"selected_buying_strategy,omitempty" bson:"selected_buying_strategy,omitempty"`
Partnerships []T `json:"partnerships,omitempty" bson:"partnerships,omitempty"` // SelectedStrategy int `json:"selected_strategy,omitempty" bson:"selected_strategy,omitempty"`
Partnerships []T `json:"partnerships,omitempty" bson:"partnerships,omitempty"`
} }
// TODO should kicks all selection
func NewInstance[T ResourcePartnerITF](name string) *ResourceInstance[T] { func NewInstance[T ResourcePartnerITF](name string) *ResourceInstance[T] {
return &ResourceInstance[T]{ return &ResourceInstance[T]{
AbstractObject: utils.AbstractObject{ AbstractObject: utils.AbstractObject{
@@ -178,40 +211,27 @@ func (ri *ResourceInstance[T]) ClearEnv() {
ri.Outputs = []models.Param{} ri.Outputs = []models.Param{}
} }
func (ri *ResourceInstance[T]) GetProfile() pricing.PricingProfileITF { func (ri *ResourceInstance[T]) GetProfile(peerID string, partnershipIndex *int, buyingIndex *int, strategyIndex *int) pricing.PricingProfileITF {
if len(ri.Partnerships) > ri.SelectedPartnershipIndex { if partnershipIndex != nil && len(ri.Partnerships) > *partnershipIndex {
prts := ri.Partnerships[ri.SelectedPartnershipIndex] prts := ri.Partnerships[*partnershipIndex]
return prts.GetProfile(ri.SelectedBuyingStrategy, ri.SelectedBuyingStrategy) return prts.GetProfile(buyingIndex, strategyIndex)
}
if ok, _ := peer.IsMySelf(peerID); ok {
return pricing.GetDefaultPricingProfile()
} }
return nil return nil
} }
func (ri *ResourceInstance[T]) GetSelectedPartnership(peerID string, groups []string) ResourcePartnerITF {
if len(ri.Partnerships) > ri.SelectedPartnershipIndex {
return ri.Partnerships[ri.SelectedPartnershipIndex]
}
return nil
}
func (ri *ResourceInstance[T]) GetPartnerships(peerID string, groups []string) []ResourcePartnerITF {
partners := []ResourcePartnerITF{}
for _, p := range ri.Partnerships {
if p.GetPeerGroups()[peerID] != nil {
for _, g := range p.GetPeerGroups()[peerID] {
if slices.Contains(groups, g) {
partners = append(partners, p)
}
}
}
}
return partners
}
func (ri *ResourceInstance[T]) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF { func (ri *ResourceInstance[T]) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF {
pricings := []pricing.PricingProfileITF{} pricings := []pricing.PricingProfileITF{}
for _, p := range ri.Partnerships { for _, p := range ri.Partnerships {
pricings = append(pricings, p.GetPricingsProfiles(peerID, groups)...) pricings = append(pricings, p.GetPricingsProfiles(peerID, groups)...)
} }
if len(pricings) == 0 {
if ok, _ := peer.IsMySelf(peerID); ok {
pricings = append(pricings, pricing.GetDefaultPricingProfile())
}
}
return pricings return pricings
} }
@@ -222,6 +242,15 @@ func (ri *ResourceInstance[T]) GetPeerGroups() ([]ResourcePartnerITF, []map[stri
partners = append(partners, p) partners = append(partners, p)
groups = append(groups, p.GetPeerGroups()) groups = append(groups, p.GetPeerGroups())
} }
if len(groups) == 0 {
_, id := peer.GetSelf()
groups = []map[string][]string{
{
id: {"*"},
},
}
// TODO make allow all only for self.
}
return partners, groups return partners, groups
} }
@@ -238,12 +267,15 @@ type ResourcePartnerShip[T pricing.PricingProfileITF] struct {
// to upgrade pricing profiles. to be a map BuyingStrategy, map of Strategy // to upgrade pricing profiles. to be a map BuyingStrategy, map of Strategy
} }
func (ri *ResourcePartnerShip[T]) GetProfile(buying int, strategy int) pricing.PricingProfileITF { func (ri *ResourcePartnerShip[T]) GetProfile(buying *int, strategy *int) pricing.PricingProfileITF {
if strat, ok := ri.PricingProfiles[buying]; ok { if buying != nil && strategy != nil {
if profile, ok := strat[strategy]; ok { if strat, ok := ri.PricingProfiles[*buying]; ok {
return profile if profile, ok := strat[*strategy]; ok {
return profile
}
} }
} }
return nil return nil
} }
@@ -269,10 +301,21 @@ func (ri *ResourcePartnerShip[T]) GetPricingsProfiles(peerID string, groups []st
return profiles return profiles
} }
} }
if len(profiles) == 0 {
if ok, _ := peer.IsMySelf(peerID); ok {
profiles = append(profiles, pricing.GetDefaultPricingProfile())
}
}
return profiles return profiles
} }
func (rp *ResourcePartnerShip[T]) GetPeerGroups() map[string][]string { func (rp *ResourcePartnerShip[T]) GetPeerGroups() map[string][]string {
if len(rp.PeerGroups) == 0 {
_, id := peer.GetSelf()
return map[string][]string{
id: {"*"},
}
}
return rp.PeerGroups return rp.PeerGroups
} }

View File

@@ -20,7 +20,7 @@ func NewAccessor[T ResourceInterface](t tools.DataType, request *tools.APIReques
if !slices.Contains([]tools.DataType{ if !slices.Contains([]tools.DataType{
tools.COMPUTE_RESOURCE, tools.STORAGE_RESOURCE, tools.COMPUTE_RESOURCE, tools.STORAGE_RESOURCE,
tools.PROCESSING_RESOURCE, tools.WORKFLOW_RESOURCE, tools.PROCESSING_RESOURCE, tools.WORKFLOW_RESOURCE,
tools.DATA_RESOURCE, tools.DATA_RESOURCE, tools.NATIVE_TOOL,
}, t) { }, t) {
return nil return nil
} }

View File

@@ -31,15 +31,18 @@ func (r *StorageResource) GetType() string {
return tools.STORAGE_RESOURCE.String() return tools.STORAGE_RESOURCE.String()
} }
func (abs *StorageResource) ConvertToPricedResource(t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { func (abs *StorageResource) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
if t != tools.STORAGE_RESOURCE { if t != tools.STORAGE_RESOURCE {
return nil return nil, errors.New("not the proper type expected : cannot convert to priced resource : have " + t.String() + " wait Storage")
}
p, err := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, request)
if err != nil {
return nil, err
} }
p := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, request)
priced := p.(*PricedResource) priced := p.(*PricedResource)
return &PricedStorageResource{ return &PricedStorageResource{
PricedResource: *priced, PricedResource: *priced,
} }, nil
} }
type StorageResourceInstance struct { type StorageResourceInstance struct {
@@ -62,32 +65,6 @@ func NewStorageResourceInstance(name string, peerID string) ResourceInstanceITF
UUID: uuid.New().String(), UUID: uuid.New().String(),
Name: name, Name: name,
}, },
Partnerships: []*StorageResourcePartnership{
{
ResourcePartnerShip: ResourcePartnerShip[*StorageResourcePricingProfile]{
Namespace: "default",
PeerGroups: map[string][]string{
peerID: {"*"},
},
PricingProfiles: map[int]map[int]*StorageResourcePricingProfile{
0: {
0: &StorageResourcePricingProfile{
ExploitPricingProfile: pricing.ExploitPricingProfile[StorageResourcePricingStrategy]{
AccessPricingProfile: pricing.AccessPricingProfile[StorageResourcePricingStrategy]{
Pricing: pricing.PricingStrategy[StorageResourcePricingStrategy]{
Price: 0,
Currency: "EUR",
TimePricingStrategy: pricing.ONCE,
BuyingStrategy: pricing.PERMANENT,
},
},
},
},
},
},
},
},
},
}, },
} }
} }
@@ -192,10 +169,6 @@ type StorageResourcePricingProfile struct {
pricing.ExploitPricingProfile[StorageResourcePricingStrategy] // ExploitPricingProfile is the pricing profile of a storage it means that we exploit the resource for an amount of continuous time pricing.ExploitPricingProfile[StorageResourcePricingStrategy] // ExploitPricingProfile is the pricing profile of a storage it means that we exploit the resource for an amount of continuous time
} }
func (p *StorageResourcePricingProfile) GetPurchase() pricing.BuyingStrategy {
return p.Pricing.BuyingStrategy
}
func (p *StorageResourcePricingProfile) IsPurchasable() bool { func (p *StorageResourcePricingProfile) IsPurchasable() bool {
return p.Pricing.BuyingStrategy != pricing.UNDEFINED_SUBSCRIPTION return p.Pricing.BuyingStrategy != pricing.UNDEFINED_SUBSCRIPTION
} }
@@ -207,10 +180,6 @@ func (p *StorageResourcePricingProfile) IsBooked() bool {
return true return true
} }
func (p *StorageResourcePricingProfile) GetPrice(amountOfData float64, val float64, start time.Time, end time.Time, params ...string) (float64, error) {
return p.Pricing.GetPrice(amountOfData, val, start, &end)
}
type PricedStorageResource struct { type PricedStorageResource struct {
PricedResource PricedResource
UsageStorageGB float64 `json:"storage_gb,omitempty" bson:"storage_gb,omitempty"` UsageStorageGB float64 `json:"storage_gb,omitempty" bson:"storage_gb,omitempty"`
@@ -220,15 +189,18 @@ func (r *PricedStorageResource) GetType() tools.DataType {
return tools.STORAGE_RESOURCE return tools.STORAGE_RESOURCE
} }
func (r *PricedStorageResource) GetPrice() (float64, error) { func (r *PricedStorageResource) GetPriceHT() (float64, error) {
fmt.Println("GetPrice", r.UsageStart, r.UsageEnd) if r.BookingConfiguration == nil {
now := time.Now() r.BookingConfiguration = &BookingConfiguration{}
if r.UsageStart == nil {
r.UsageStart = &now
} }
if r.UsageEnd == nil { fmt.Println("GetPriceHT", r.BookingConfiguration.UsageStart, r.BookingConfiguration.UsageEnd)
add := r.UsageStart.Add(time.Duration(1 * time.Hour)) now := time.Now()
r.UsageEnd = &add if r.BookingConfiguration.UsageStart == nil {
r.BookingConfiguration.UsageStart = &now
}
if r.BookingConfiguration.UsageEnd == nil {
add := r.BookingConfiguration.UsageStart.Add(time.Duration(1 * time.Hour))
r.BookingConfiguration.UsageEnd = &add
} }
if r.SelectedPricing == nil { if r.SelectedPricing == nil {
return 0, errors.New("pricing profile must be set on Priced Storage" + r.ResourceID) return 0, errors.New("pricing profile must be set on Priced Storage" + r.ResourceID)
@@ -242,5 +214,6 @@ func (r *PricedStorageResource) GetPrice() (float64, error) {
return 0, err return 0, err
} }
} }
return pricing.GetPrice(amountOfData, r.ExplicitBookingDurationS, *r.UsageStart, *r.UsageEnd) return pricing.GetPriceHT(amountOfData, r.BookingConfiguration.ExplicitBookingDurationS,
*r.BookingConfiguration.UsageStart, *r.BookingConfiguration.UsageEnd, r.Variations)
} }

View File

@@ -30,12 +30,12 @@ func TestComputeResource_ConvertToPricedResource(t *testing.T) {
cr := &resources.ComputeResource{} cr := &resources.ComputeResource{}
cr.UUID = "comp123" cr.UUID = "comp123"
cr.AbstractInstanciatedResource.UUID = cr.UUID cr.AbstractInstanciatedResource.UUID = cr.UUID
result := cr.ConvertToPricedResource(tools.COMPUTE_RESOURCE, req) result, _ := cr.ConvertToPricedResource(tools.COMPUTE_RESOURCE, nil, nil, nil, nil, nil, req)
assert.NotNil(t, result) assert.NotNil(t, result)
assert.IsType(t, &resources.PricedComputeResource{}, result) assert.IsType(t, &resources.PricedComputeResource{}, result)
} }
func TestComputeResourcePricingProfile_GetPrice_CPUs(t *testing.T) { func TestComputeResourcePricingProfile_GetPriceHT_CPUs(t *testing.T) {
start := time.Now() start := time.Now()
end := start.Add(1 * time.Hour) end := start.Add(1 * time.Hour)
profile := resources.ComputeResourcePricingProfile{ profile := resources.ComputeResourcePricingProfile{
@@ -47,45 +47,47 @@ func TestComputeResourcePricingProfile_GetPrice_CPUs(t *testing.T) {
}, },
} }
price, err := profile.GetPrice(2, 3600, start, end, "cpus", "Xeon") price, err := profile.GetPriceHT(2, 3600, start, end, []*pricing.PricingVariation{}, "cpus", "Xeon")
require.NoError(t, err) require.NoError(t, err)
assert.Greater(t, price, float64(0)) assert.Greater(t, price, float64(0))
} }
func TestComputeResourcePricingProfile_GetPrice_InvalidParams(t *testing.T) { func TestComputeResourcePricingProfile_GetPriceHT_InvalidParams(t *testing.T) {
profile := resources.ComputeResourcePricingProfile{} profile := resources.ComputeResourcePricingProfile{}
_, err := profile.GetPrice(1, 3600, time.Now(), time.Now()) _, err := profile.GetPriceHT(1, 3600, time.Now(), time.Now(), []*pricing.PricingVariation{})
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, "params must be set", err.Error()) assert.Equal(t, "params must be set", err.Error())
} }
func TestPricedComputeResource_GetPrice(t *testing.T) { func TestPricedComputeResource_GetPriceHT(t *testing.T) {
start := time.Now() start := time.Now()
end := start.Add(1 * time.Hour) end := start.Add(1 * time.Hour)
r := resources.PricedComputeResource{ r := resources.PricedComputeResource{
PricedResource: resources.PricedResource{ PricedResource: resources.PricedResource{
ResourceID: "comp456", ResourceID: "comp456",
UsageStart: &start, BookingConfiguration: &resources.BookingConfiguration{
UsageEnd: &end, UsageStart: &start,
ExplicitBookingDurationS: 3600, UsageEnd: &end,
ExplicitBookingDurationS: 3600,
},
}, },
CPUsLocated: map[string]float64{"Xeon": 2}, CPUsLocated: map[string]float64{"Xeon": 2},
GPUsLocated: map[string]float64{"Tesla": 1}, GPUsLocated: map[string]float64{"Tesla": 1},
RAMLocated: 4, RAMLocated: 4,
} }
price, err := r.GetPrice() price, err := r.GetPriceHT()
require.NoError(t, err) require.NoError(t, err)
assert.Greater(t, price, float64(0)) assert.Greater(t, price, float64(0))
} }
func TestPricedComputeResource_GetPrice_MissingProfile(t *testing.T) { func TestPricedComputeResource_GetPriceHT_MissingProfile(t *testing.T) {
r := resources.PricedComputeResource{ r := resources.PricedComputeResource{
PricedResource: resources.PricedResource{ PricedResource: resources.PricedResource{
ResourceID: "comp789", ResourceID: "comp789",
}, },
} }
_, err := r.GetPrice() _, err := r.GetPriceHT()
require.Error(t, err) require.Error(t, err)
assert.Contains(t, err.Error(), "pricing profile must be set") assert.Contains(t, err.Error(), "pricing profile must be set")
} }

View File

@@ -27,10 +27,10 @@ func TestDataResource_GetAccessor(t *testing.T) {
func TestDataResource_ConvertToPricedResource(t *testing.T) { func TestDataResource_ConvertToPricedResource(t *testing.T) {
d := &resources.DataResource{} d := &resources.DataResource{}
d.UUID = "123" d.UUID = "123"
res := d.ConvertToPricedResource(tools.DATA_RESOURCE, &tools.APIRequest{}) res, _ := d.ConvertToPricedResource(tools.DATA_RESOURCE, nil, nil, nil, nil, nil, &tools.APIRequest{})
assert.IsType(t, &resources.PricedDataResource{}, res) assert.IsType(t, &resources.PricedDataResource{}, res)
nilRes := d.ConvertToPricedResource(tools.PROCESSING_RESOURCE, &tools.APIRequest{}) nilRes, _ := d.ConvertToPricedResource(tools.PROCESSING_RESOURCE, nil, nil, nil, nil, nil, &tools.APIRequest{})
assert.Nil(t, nilRes) assert.Nil(t, nilRes)
} }
@@ -80,7 +80,7 @@ func TestDataResourcePricingProfile_IsPurchased(t *testing.T) {
assert.True(t, profile.IsPurchasable()) assert.True(t, profile.IsPurchasable())
} }
func TestPricedDataResource_GetPrice(t *testing.T) { func TestPricedDataResource_GetPriceHT(t *testing.T) {
now := time.Now() now := time.Now()
later := now.Add(1 * time.Hour) later := now.Add(1 * time.Hour)
mockPrice := 42.0 mockPrice := 42.0
@@ -92,23 +92,25 @@ func TestPricedDataResource_GetPrice(t *testing.T) {
r := &resources.PricedDataResource{ r := &resources.PricedDataResource{
PricedResource: resources.PricedResource{ PricedResource: resources.PricedResource{
UsageStart: &now, BookingConfiguration: &resources.BookingConfiguration{
UsageEnd: &later, UsageStart: &now,
UsageEnd: &later,
},
}, },
} }
price, err := r.GetPrice() price, err := r.GetPriceHT()
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, mockPrice, price) assert.Equal(t, mockPrice, price)
} }
func TestPricedDataResource_GetPrice_NoProfiles(t *testing.T) { func TestPricedDataResource_GetPriceHT_NoProfiles(t *testing.T) {
r := &resources.PricedDataResource{ r := &resources.PricedDataResource{
PricedResource: resources.PricedResource{ PricedResource: resources.PricedResource{
ResourceID: "test-resource", ResourceID: "test-resource",
}, },
} }
_, err := r.GetPrice() _, err := r.GetPriceHT()
assert.Error(t, err) assert.Error(t, err)
assert.Contains(t, err.Error(), "pricing profile must be set") assert.Contains(t, err.Error(), "pricing profile must be set")
} }

View File

@@ -26,7 +26,7 @@ func (m *MockPricingProfile) IsPurchasable() bool {
return m.Purchased return m.Purchased
} }
func (m *MockPricingProfile) GetPrice(amount float64, explicitDuration float64, start time.Time, end time.Time, _ ...string) (float64, error) { func (m *MockPricingProfile) GetPriceHT(amount float64, explicitDuration float64, start time.Time, end time.Time, variations []*pricing.PricingVariation, _ ...string) (float64, error) {
if m.ReturnErr { if m.ReturnErr {
return 0, errors.New("mock error") return 0, errors.New("mock error")
} }
@@ -72,14 +72,21 @@ func TestGetAndSetLocationStartEnd(t *testing.T) {
func TestGetExplicitDurationInS(t *testing.T) { func TestGetExplicitDurationInS(t *testing.T) {
t.Run("uses explicit duration if set", func(t *testing.T) { t.Run("uses explicit duration if set", func(t *testing.T) {
r := &resources.PricedResource{ExplicitBookingDurationS: 3600} r := &resources.PricedResource{BookingConfiguration: &resources.BookingConfiguration{
ExplicitBookingDurationS: 3600,
},
}
assert.Equal(t, 3600.0, r.GetExplicitDurationInS()) assert.Equal(t, 3600.0, r.GetExplicitDurationInS())
}) })
t.Run("computes duration from start and end", func(t *testing.T) { t.Run("computes duration from start and end", func(t *testing.T) {
start := time.Now() start := time.Now()
end := start.Add(2 * time.Hour) end := start.Add(2 * time.Hour)
r := &resources.PricedResource{UsageStart: &start, UsageEnd: &end} r := &resources.PricedResource{
BookingConfiguration: &resources.BookingConfiguration{
UsageStart: &start, UsageEnd: &end,
},
}
assert.InDelta(t, 7200.0, r.GetExplicitDurationInS(), 0.1) assert.InDelta(t, 7200.0, r.GetExplicitDurationInS(), 0.1)
}) })
@@ -89,10 +96,10 @@ func TestGetExplicitDurationInS(t *testing.T) {
}) })
} }
func TestGetPrice(t *testing.T) { func TestGetPriceHT(t *testing.T) {
t.Run("returns error if no pricing profile", func(t *testing.T) { t.Run("returns error if no pricing profile", func(t *testing.T) {
r := &resources.PricedResource{ResourceID: "no-profile"} r := &resources.PricedResource{ResourceID: "no-profile"}
price, err := r.GetPrice() price, err := r.GetPriceHT()
require.Error(t, err) require.Error(t, err)
assert.Contains(t, err.Error(), "pricing profile must be set") assert.Contains(t, err.Error(), "pricing profile must be set")
assert.Equal(t, 0.0, price) assert.Equal(t, 0.0, price)
@@ -102,24 +109,28 @@ func TestGetPrice(t *testing.T) {
start := time.Now() start := time.Now()
end := start.Add(30 * time.Minute) end := start.Add(30 * time.Minute)
r := &resources.PricedResource{ r := &resources.PricedResource{
UsageStart: &start, BookingConfiguration: &resources.BookingConfiguration{
UsageEnd: &end, UsageStart: &start,
UsageEnd: &end,
},
} }
price, err := r.GetPrice() price, err := r.GetPriceHT()
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 42.0, price) assert.Equal(t, 42.0, price)
}) })
t.Run("returns error if profile GetPrice fails", func(t *testing.T) { t.Run("returns error if profile GetPriceHT fails", func(t *testing.T) {
start := time.Now() start := time.Now()
end := start.Add(1 * time.Hour) end := start.Add(1 * time.Hour)
mock := &MockPricingProfile{ReturnErr: true} mock := &MockPricingProfile{ReturnErr: true}
r := &resources.PricedResource{ r := &resources.PricedResource{
SelectedPricing: mock, SelectedPricing: mock,
UsageStart: &start, BookingConfiguration: &resources.BookingConfiguration{
UsageEnd: &end, UsageStart: &start,
UsageEnd: &end,
},
} }
price, err := r.GetPrice() price, err := r.GetPriceHT()
require.Error(t, err) require.Error(t, err)
assert.Equal(t, 0.0, price) assert.Equal(t, 0.0, price)
}) })
@@ -130,10 +141,12 @@ func TestGetPrice(t *testing.T) {
mock := &MockPricingProfile{ReturnCost: 10.0} mock := &MockPricingProfile{ReturnCost: 10.0}
r := &resources.PricedResource{ r := &resources.PricedResource{
SelectedPricing: mock, SelectedPricing: mock,
UsageStart: &start, BookingConfiguration: &resources.BookingConfiguration{
UsageEnd: &end, UsageStart: &start,
UsageEnd: &end,
},
} }
price, err := r.GetPrice() price, err := r.GetPriceHT()
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 10.0, price) assert.Equal(t, 10.0, price)
}) })

View File

@@ -5,6 +5,7 @@ import (
"time" "time"
"cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/resources"
. "cloud.o-forge.io/core/oc-lib/models/resources" . "cloud.o-forge.io/core/oc-lib/models/resources"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -40,7 +41,9 @@ func TestPricedProcessingResource_GetExplicitDurationInS(t *testing.T) {
name: "Nil start time, non-service", name: "Nil start time, non-service",
input: PricedProcessingResource{ input: PricedProcessingResource{
PricedResource: PricedResource{ PricedResource: PricedResource{
UsageStart: nil, BookingConfiguration: &resources.BookingConfiguration{
UsageStart: nil,
},
}, },
}, },
expected: float64((1 * time.Hour).Seconds()), expected: float64((1 * time.Hour).Seconds()),
@@ -49,8 +52,10 @@ func TestPricedProcessingResource_GetExplicitDurationInS(t *testing.T) {
name: "Duration computed from start and end", name: "Duration computed from start and end",
input: PricedProcessingResource{ input: PricedProcessingResource{
PricedResource: PricedResource{ PricedResource: PricedResource{
UsageStart: &now, BookingConfiguration: &resources.BookingConfiguration{
UsageEnd: &after, UsageStart: &now,
UsageEnd: &after,
},
}, },
}, },
expected: float64((2 * time.Hour).Seconds()), expected: float64((2 * time.Hour).Seconds()),
@@ -59,7 +64,9 @@ func TestPricedProcessingResource_GetExplicitDurationInS(t *testing.T) {
name: "Explicit duration takes precedence", name: "Explicit duration takes precedence",
input: PricedProcessingResource{ input: PricedProcessingResource{
PricedResource: PricedResource{ PricedResource: PricedResource{
ExplicitBookingDurationS: 1337, BookingConfiguration: &resources.BookingConfiguration{
ExplicitBookingDurationS: 1337,
},
}, },
}, },
expected: 1337, expected: 1337,
@@ -80,7 +87,7 @@ func TestProcessingResource_GetAccessor(t *testing.T) {
assert.NotNil(t, acc) assert.NotNil(t, acc)
} }
func TestProcessingResourcePricingProfile_GetPrice(t *testing.T) { func TestProcessingResourcePricingProfile_GetPriceHT(t *testing.T) {
start := time.Now() start := time.Now()
end := start.Add(2 * time.Hour) end := start.Add(2 * time.Hour)
mockPricing := pricing.AccessPricingProfile[pricing.TimePricingStrategy]{ mockPricing := pricing.AccessPricingProfile[pricing.TimePricingStrategy]{
@@ -88,8 +95,8 @@ func TestProcessingResourcePricingProfile_GetPrice(t *testing.T) {
Price: 100.0, Price: 100.0,
}, },
} }
profile := &ProcessingResourcePricingProfile{mockPricing} profile := &ProcessingResourcePricingProfile{AccessPricingProfile: mockPricing}
price, err := profile.GetPrice(0, 0, start, end) price, err := profile.GetPriceHT(0, 0, start, end, []*pricing.PricingVariation{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 100.0, price) assert.Equal(t, 100.0, price)
} }

View File

@@ -20,7 +20,7 @@ func (m *MockInstance) GetID() string { return m.ID }
func (m *MockInstance) GetName() string { return m.Name } func (m *MockInstance) GetName() string { return m.Name }
func (m *MockInstance) ClearEnv() {} func (m *MockInstance) ClearEnv() {}
func (m *MockInstance) ClearPeerGroups() {} func (m *MockInstance) ClearPeerGroups() {}
func (m *MockInstance) GetProfile() pricing.PricingProfileITF { func (m *MockInstance) GetProfile(peerID string, a *int, b *int, c *int) pricing.PricingProfileITF {
return nil return nil
} }
func (m *MockInstance) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF { func (m *MockInstance) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF {
@@ -36,7 +36,7 @@ type MockPartner struct {
groups map[string][]string groups map[string][]string
} }
func (m *MockPartner) GetProfile(buying int, strategy int) pricing.PricingProfileITF { func (m *MockPartner) GetProfile(buying *int, strategy *int) pricing.PricingProfileITF {
return nil return nil
} }
@@ -62,10 +62,10 @@ func TestGetSelectedInstance_WithValidIndex(t *testing.T) {
inst1 := &MockInstance{ID: "1"} inst1 := &MockInstance{ID: "1"}
inst2 := &MockInstance{ID: "2"} inst2 := &MockInstance{ID: "2"}
resource := &resources.AbstractInstanciatedResource[*MockInstance]{ resource := &resources.AbstractInstanciatedResource[*MockInstance]{
AbstractResource: resources.AbstractResource{SelectedInstanceIndex: &index}, AbstractResource: resources.AbstractResource{},
Instances: []*MockInstance{inst1, inst2}, Instances: []*MockInstance{inst1, inst2},
} }
result := resource.GetSelectedInstance() result := resource.GetSelectedInstance(&index)
assert.Equal(t, inst2, result) assert.Equal(t, inst2, result)
} }
@@ -74,7 +74,7 @@ func TestGetSelectedInstance_NoIndex(t *testing.T) {
resource := &resources.AbstractInstanciatedResource[*MockInstance]{ resource := &resources.AbstractInstanciatedResource[*MockInstance]{
Instances: []*MockInstance{inst}, Instances: []*MockInstance{inst},
} }
result := resource.GetSelectedInstance() result := resource.GetSelectedInstance(nil)
assert.Equal(t, inst, result) assert.Equal(t, inst, result)
} }
@@ -103,9 +103,9 @@ type FakeResource struct {
resources.AbstractInstanciatedResource[*MockInstance] resources.AbstractInstanciatedResource[*MockInstance]
} }
func (f *FakeResource) Trim() {} func (f *FakeResource) Trim() {}
func (f *FakeResource) SetAllowedInstances(*tools.APIRequest) {} func (f *FakeResource) SetAllowedInstances(*tools.APIRequest) {}
func (f *FakeResource) VerifyAuth(*tools.APIRequest) bool { return true } func (f *FakeResource) VerifyAuth(string, *tools.APIRequest) bool { return true }
func TestNewAccessor_ReturnsValid(t *testing.T) { func TestNewAccessor_ReturnsValid(t *testing.T) {
acc := resources.NewAccessor[*FakeResource](tools.COMPUTE_RESOURCE, &tools.APIRequest{}, func() utils.DBObject { acc := resources.NewAccessor[*FakeResource](tools.COMPUTE_RESOURCE, &tools.APIRequest{}, func() utils.DBObject {

View File

@@ -26,14 +26,14 @@ func TestStorageResource_ConvertToPricedResource_ValidType(t *testing.T) {
res := &resources.StorageResource{} res := &resources.StorageResource{}
res.AbstractInstanciatedResource.CreatorID = "creator" res.AbstractInstanciatedResource.CreatorID = "creator"
res.AbstractInstanciatedResource.UUID = "res-id" res.AbstractInstanciatedResource.UUID = "res-id"
priced := res.ConvertToPricedResource(tools.STORAGE_RESOURCE, &tools.APIRequest{}) priced, _ := res.ConvertToPricedResource(tools.STORAGE_RESOURCE, nil, nil, nil, nil, nil, &tools.APIRequest{})
assert.NotNil(t, priced) assert.NotNil(t, priced)
assert.IsType(t, &resources.PricedStorageResource{}, priced) assert.IsType(t, &resources.PricedStorageResource{}, priced)
} }
func TestStorageResource_ConvertToPricedResource_InvalidType(t *testing.T) { func TestStorageResource_ConvertToPricedResource_InvalidType(t *testing.T) {
res := &resources.StorageResource{} res := &resources.StorageResource{}
priced := res.ConvertToPricedResource(tools.COMPUTE_RESOURCE, &tools.APIRequest{}) priced, _ := res.ConvertToPricedResource(tools.COMPUTE_RESOURCE, nil, nil, nil, nil, nil, &tools.APIRequest{})
assert.Nil(t, priced) assert.Nil(t, priced)
} }
@@ -94,12 +94,12 @@ func TestStorageResourcePricingStrategy_GetQuantity_Invalid(t *testing.T) {
assert.Equal(t, 0.0, q) assert.Equal(t, 0.0, q)
} }
func TestPricedStorageResource_GetPrice_NoProfiles(t *testing.T) { func TestPricedStorageResource_GetPriceHT_NoProfiles(t *testing.T) {
res := &resources.PricedStorageResource{ res := &resources.PricedStorageResource{
PricedResource: resources.PricedResource{ PricedResource: resources.PricedResource{
ResourceID: "res-id", ResourceID: "res-id",
}, },
} }
_, err := res.GetPrice() _, err := res.GetPriceHT()
assert.Error(t, err) assert.Error(t, err)
} }

View File

@@ -32,7 +32,7 @@ func TestWorkflowResource_ConvertToPricedResource(t *testing.T) {
Groups: []string{"group1"}, Groups: []string{"group1"},
} }
pr := w.ConvertToPricedResource(tools.WORKFLOW_RESOURCE, req) pr, _ := w.ConvertToPricedResource(tools.WORKFLOW_RESOURCE, nil, nil, nil, nil, nil, req)
assert.Equal(t, "creator-id", pr.GetCreatorID()) assert.Equal(t, "creator-id", pr.GetCreatorID())
assert.Equal(t, tools.WORKFLOW_RESOURCE, pr.GetType()) assert.Equal(t, tools.WORKFLOW_RESOURCE, pr.GetType())
} }

View File

@@ -37,12 +37,13 @@ func (w *WorkflowResource) SetAllowedInstances(request *tools.APIRequest) {
/* EMPTY */ /* EMPTY */
} }
func (w *WorkflowResource) ConvertToPricedResource(t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { func (w *WorkflowResource) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
return &PricedResource{ return &PricedResource{
Name: w.Name, Name: w.Name,
Logo: w.Logo, Logo: w.Logo,
ResourceID: w.UUID, ResourceID: w.UUID,
ResourceType: t, ResourceType: t,
Quantity: 1,
CreatorID: w.CreatorID, CreatorID: w.CreatorID,
} }, nil // TODO ???
} }

View File

@@ -98,8 +98,8 @@ func (ao *AbstractObject) UpToDate(user string, peer string, create bool) {
} }
} }
func (ao *AbstractObject) VerifyAuth(request *tools.APIRequest) bool { func (ao *AbstractObject) VerifyAuth(callName string, request *tools.APIRequest) bool {
return ao.AccessMode == Public || (request != nil && ao.CreatorID == request.PeerID && request.PeerID != "") return (ao.AccessMode == Public && callName == "get") || request.Admin || (request != nil && ao.CreatorID == request.PeerID && request.PeerID != "")
} }
func (ao *AbstractObject) GetObjectFilters(search string) *dbs.Filters { func (ao *AbstractObject) GetObjectFilters(search string) *dbs.Filters {

View File

@@ -18,7 +18,7 @@ func VerifyAccess(a Accessor, id string) error {
if err != nil { if err != nil {
return err return err
} }
if a.ShouldVerifyAuth() && !data.VerifyAuth(a.GetRequest()) { if a.ShouldVerifyAuth() && !data.VerifyAuth("get", a.GetRequest()) {
return errors.New("you are not allowed to access :" + a.GetType().String()) return errors.New("you are not allowed to access :" + a.GetType().String())
} }
return nil return nil
@@ -41,7 +41,7 @@ func GenericStoreOne(data DBObject, a Accessor) (DBObject, int, error) {
}}, }},
}, },
} }
if a.ShouldVerifyAuth() && !data.VerifyAuth(a.GetRequest()) { if a.ShouldVerifyAuth() && !data.VerifyAuth("store", a.GetRequest()) {
return nil, 403, errors.New("you are not allowed to access : " + a.GetType().String()) return nil, 403, errors.New("you are not allowed to access : " + a.GetType().String())
} }
if cursor, _, _ := a.Search(&f, "", data.IsDrafted()); len(cursor) > 0 { if cursor, _, _ := a.Search(&f, "", data.IsDrafted()); len(cursor) > 0 {
@@ -68,7 +68,7 @@ func GenericDeleteOne(id string, a Accessor) (DBObject, int, error) {
if err != nil { if err != nil {
return nil, code, err return nil, code, err
} }
if a.ShouldVerifyAuth() && !res.VerifyAuth(a.GetRequest()) { if a.ShouldVerifyAuth() && !res.VerifyAuth("delete", a.GetRequest()) {
return nil, 403, errors.New("you are not allowed to access " + a.GetType().String()) return nil, 403, errors.New("you are not allowed to access " + a.GetType().String())
} }
_, code, err = mongo.MONGOService.DeleteOne(id, a.GetType().String()) _, code, err = mongo.MONGOService.DeleteOne(id, a.GetType().String())
@@ -92,7 +92,7 @@ func GenericUpdateOne(set DBObject, id string, a Accessor, new DBObject) (DBObje
} }
set = newSet set = newSet
r.UpToDate(a.GetUser(), a.GetPeerID(), false) r.UpToDate(a.GetUser(), a.GetPeerID(), false)
if a.ShouldVerifyAuth() && !r.VerifyAuth(a.GetRequest()) { if a.ShouldVerifyAuth() && !r.VerifyAuth("update", a.GetRequest()) {
return nil, 403, errors.New("you are not allowed to access :" + a.GetType().String()) return nil, 403, errors.New("you are not allowed to access :" + a.GetType().String())
} }
change := set.Serialize(set) // get the changes change := set.Serialize(set) // get the changes
@@ -116,7 +116,7 @@ func GenericLoadOne[T DBObject](id string, f func(DBObject) (DBObject, int, erro
return nil, code, err return nil, code, err
} }
res_mongo.Decode(&data) res_mongo.Decode(&data)
if a.ShouldVerifyAuth() && !data.VerifyAuth(a.GetRequest()) { if a.ShouldVerifyAuth() && !data.VerifyAuth("get", a.GetRequest()) {
return nil, 403, errors.New("you are not allowed to access :" + a.GetType().String()) return nil, 403, errors.New("you are not allowed to access :" + a.GetType().String())
} }
return f(data) return f(data)
@@ -132,7 +132,7 @@ func genericLoadAll[T DBObject](res *mgb.Cursor, code int, err error, onlyDraft
return nil, 404, err return nil, 404, err
} }
for _, r := range results { for _, r := range results {
if (a.ShouldVerifyAuth() && !r.VerifyAuth(a.GetRequest())) || f(r) == nil || (onlyDraft && !r.IsDrafted()) || (!onlyDraft && r.IsDrafted()) { if (a.ShouldVerifyAuth() && !r.VerifyAuth("get", a.GetRequest())) || f(r) == nil || (onlyDraft && !r.IsDrafted()) || (!onlyDraft && r.IsDrafted()) {
continue continue
} }
objs = append(objs, f(r)) objs = append(objs, f(r))

View File

@@ -28,7 +28,7 @@ type DBObject interface {
GetCreatorID() string GetCreatorID() string
UpToDate(user string, peer string, create bool) UpToDate(user string, peer string, create bool)
CanUpdate(set DBObject) (bool, DBObject) CanUpdate(set DBObject) (bool, DBObject)
VerifyAuth(request *tools.APIRequest) bool VerifyAuth(callName string, request *tools.APIRequest) bool
Serialize(obj DBObject) map[string]interface{} Serialize(obj DBObject) map[string]interface{}
GetAccessor(request *tools.APIRequest) Accessor GetAccessor(request *tools.APIRequest) Accessor
Deserialize(j map[string]interface{}, obj DBObject) DBObject Deserialize(j map[string]interface{}, obj DBObject) DBObject

View File

@@ -87,14 +87,14 @@ func TestUpToDate_CreateTrue(t *testing.T) {
func TestVerifyAuth(t *testing.T) { func TestVerifyAuth(t *testing.T) {
request := &tools.APIRequest{PeerID: "peer123"} request := &tools.APIRequest{PeerID: "peer123"}
ao := &utils.AbstractObject{CreatorID: "peer123"} ao := &utils.AbstractObject{CreatorID: "peer123"}
assert.True(t, ao.VerifyAuth(request)) assert.True(t, ao.VerifyAuth("get", request))
ao = &utils.AbstractObject{AccessMode: utils.Public} ao = &utils.AbstractObject{AccessMode: utils.Public}
assert.True(t, ao.VerifyAuth(nil)) assert.True(t, ao.VerifyAuth("get", nil))
ao = &utils.AbstractObject{AccessMode: utils.Private, CreatorID: "peer123"} ao = &utils.AbstractObject{AccessMode: utils.Private, CreatorID: "peer123"}
request = &tools.APIRequest{PeerID: "wrong"} request = &tools.APIRequest{PeerID: "wrong"}
assert.False(t, ao.VerifyAuth(request)) assert.False(t, ao.VerifyAuth("get", request))
} }
func TestGetObjectFilters(t *testing.T) { func TestGetObjectFilters(t *testing.T) {

View File

@@ -47,6 +47,10 @@ func (wf *Graph) IsProcessing(item GraphItem) bool {
return item.Processing != nil return item.Processing != nil
} }
func (wf *Graph) IsNativeTool(item GraphItem) bool {
return item.NativeTool != nil
}
func (wf *Graph) IsCompute(item GraphItem) bool { func (wf *Graph) IsCompute(item GraphItem) bool {
return item.Compute != nil return item.Compute != nil
} }
@@ -64,7 +68,7 @@ func (wf *Graph) IsWorkflow(item GraphItem) bool {
} }
func (g *Graph) GetAverageTimeRelatedToProcessingActivity(start time.Time, processings []*resources.ProcessingResource, resource resources.ResourceInterface, func (g *Graph) GetAverageTimeRelatedToProcessingActivity(start time.Time, processings []*resources.ProcessingResource, resource resources.ResourceInterface,
f func(GraphItem) resources.ResourceInterface, request *tools.APIRequest) (float64, float64) { f func(GraphItem) resources.ResourceInterface, instance int, bookingMode int, request *tools.APIRequest) (float64, float64, error) {
nearestStart := float64(10000000000) nearestStart := float64(10000000000)
oneIsInfinite := false oneIsInfinite := false
longestDuration := float64(0) longestDuration := float64(0)
@@ -76,7 +80,10 @@ func (g *Graph) GetAverageTimeRelatedToProcessingActivity(start time.Time, proce
} else if link.Source.ID == processing.GetID() && f(g.Items[link.Source.ID]) != nil && f(g.Items[link.Source.ID]).GetID() == resource.GetID() { // if the source is the processing and the destination is not a compute } else if link.Source.ID == processing.GetID() && f(g.Items[link.Source.ID]) != nil && f(g.Items[link.Source.ID]).GetID() == resource.GetID() { // if the source is the processing and the destination is not a compute
source = link.Destination.ID source = link.Destination.ID
} }
priced := processing.ConvertToPricedResource(tools.PROCESSING_RESOURCE, request) priced, err := processing.ConvertToPricedResource(tools.PROCESSING_RESOURCE, &instance, &bookingMode, request)
if err != nil {
return 0, 0, err
}
if source != "" { if source != "" {
if priced.GetLocationStart() != nil { if priced.GetLocationStart() != nil {
near := float64(priced.GetLocationStart().Sub(start).Seconds()) near := float64(priced.GetLocationStart().Sub(start).Seconds())
@@ -97,15 +104,15 @@ func (g *Graph) GetAverageTimeRelatedToProcessingActivity(start time.Time, proce
} }
} }
if oneIsInfinite { if oneIsInfinite {
return nearestStart, -1 return nearestStart, -1, nil
} }
return nearestStart, longestDuration return nearestStart, longestDuration, nil
} }
/* /*
* GetAverageTimeBeforeStart is a function that returns the average time before the start of a processing * GetAverageTimeBeforeStart is a function that returns the average time before the start of a processing
*/ */
func (g *Graph) GetAverageTimeProcessingBeforeStart(average float64, processingID string, request *tools.APIRequest) float64 { func (g *Graph) GetAverageTimeProcessingBeforeStart(average float64, processingID string, instance int, bookingMode int, request *tools.APIRequest) (float64, error) {
currents := []float64{} // list of current time currents := []float64{} // list of current time
for _, link := range g.Links { // for each link for _, link := range g.Links { // for each link
var source string // source is the source of the link var source string // source is the source of the link
@@ -121,13 +128,20 @@ func (g *Graph) GetAverageTimeProcessingBeforeStart(average float64, processingI
if r == nil { // if item is nil, continue if r == nil { // if item is nil, continue
continue continue
} }
priced := r.ConvertToPricedResource(dt, request) priced, err := r.ConvertToPricedResource(dt, &instance, &bookingMode, request)
if err != nil {
return 0, err
}
current := priced.GetExplicitDurationInS() // get the explicit duration of the item current := priced.GetExplicitDurationInS() // get the explicit duration of the item
if current < 0 { // if current is negative, its means that duration of a before could be infinite continue if current < 0 { // if current is negative, its means that duration of a before could be infinite continue
return current return current, nil
} }
current += g.GetAverageTimeProcessingBeforeStart(current, source, request) // get the average time before start of the source add, err := g.GetAverageTimeProcessingBeforeStart(current, source, instance, bookingMode, request)
currents = append(currents, current) // append the current to the currents if err != nil {
return 0, err
}
current += add // get the average time before start of the source
currents = append(currents, current) // append the current to the currents
} }
var max float64 // get the max time to wait dependancies to finish var max float64 // get the max time to wait dependancies to finish
for _, current := range currents { for _, current := range currents {
@@ -135,12 +149,14 @@ func (g *Graph) GetAverageTimeProcessingBeforeStart(average float64, processingI
max = current max = current
} }
} }
return max return max, nil
} }
func (g *Graph) GetResource(id string) (tools.DataType, resources.ResourceInterface) { func (g *Graph) GetResource(id string) (tools.DataType, resources.ResourceInterface) {
if item, ok := g.Items[id]; ok { if item, ok := g.Items[id]; ok {
if item.Data != nil { if item.Data != nil {
return tools.NATIVE_TOOL, item.NativeTool
} else if item.Data != nil {
return tools.DATA_RESOURCE, item.Data return tools.DATA_RESOURCE, item.Data
} else if item.Compute != nil { } else if item.Compute != nil {
return tools.COMPUTE_RESOURCE, item.Compute return tools.COMPUTE_RESOURCE, item.Compute

View File

@@ -9,6 +9,7 @@ import (
"strings" "strings"
"time" "time"
"cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/collaborative_area/shallow_collaborative_area" "cloud.o-forge.io/core/oc-lib/models/collaborative_area/shallow_collaborative_area"
"cloud.o-forge.io/core/oc-lib/models/common" "cloud.o-forge.io/core/oc-lib/models/common"
"cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/common/pricing"
@@ -20,6 +21,16 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
type ConfigItem map[string]int
func (c ConfigItem) Get(key string) *int {
i := 0
if ins, ok := c[key]; ok {
i = ins
}
return &i
}
/* /*
* Workflow is a struct that represents a workflow * Workflow is a struct that represents a workflow
* it defines the native workflow * it defines the native workflow
@@ -40,6 +51,11 @@ func (d *Workflow) GetAccessor(request *tools.APIRequest) utils.Accessor {
func (d *Workflow) GetResources(dt tools.DataType) []resources.ResourceInterface { func (d *Workflow) GetResources(dt tools.DataType) []resources.ResourceInterface {
itf := []resources.ResourceInterface{} itf := []resources.ResourceInterface{}
switch dt { switch dt {
case tools.NATIVE_TOOL:
for _, d := range d.NativeTools {
itf = append(itf, d)
}
return itf
case tools.DATA_RESOURCE: case tools.DATA_RESOURCE:
for _, d := range d.DataResources { for _, d := range d.DataResources {
itf = append(itf, d) itf = append(itf, d)
@@ -134,19 +150,25 @@ func (d *Workflow) ExtractFromPlantUML(plantUML multipart.File, request *tools.A
graphVarName[varName] = graphItem graphVarName[varName] = graphItem
continue continue
} else if strings.Contains(line, n+"-->") { } else if strings.Contains(line, n+"-->") {
err := d.extractLink(line, graphVarName, "-->") err := d.extractLink(line, graphVarName, "-->", false)
if err != nil {
fmt.Println(err)
continue
}
} else if strings.Contains(line, n+"<--") {
err := d.extractLink(line, graphVarName, "<--", true)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
continue continue
} }
} else if strings.Contains(line, n+"--") { } else if strings.Contains(line, n+"--") {
err := d.extractLink(line, graphVarName, "--") err := d.extractLink(line, graphVarName, "--", false)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
continue continue
} }
} else if strings.Contains(line, n+"-") { } else if strings.Contains(line, n+"-") {
err := d.extractLink(line, graphVarName, "-") err := d.extractLink(line, graphVarName, "-", false)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
continue continue
@@ -176,7 +198,7 @@ func (d *Workflow) generateResource(datas []resources.ResourceInterface, request
return nil return nil
} }
func (d *Workflow) extractLink(line string, graphVarName map[string]*graph.GraphItem, pattern string) error { func (d *Workflow) extractLink(line string, graphVarName map[string]*graph.GraphItem, pattern string, reverse bool) error {
splitted := strings.Split(line, pattern) splitted := strings.Split(line, pattern)
if len(splitted) < 2 { if len(splitted) < 2 {
return errors.New("links elements not found") return errors.New("links elements not found")
@@ -199,6 +221,11 @@ func (d *Workflow) extractLink(line string, graphVarName map[string]*graph.Graph
Y: 0, Y: 0,
}, },
} }
if reverse {
tmp := link.Destination
link.Destination = link.Source
link.Source = tmp
}
splittedComments := strings.Split(line, "'") splittedComments := strings.Split(line, "'")
if len(splittedComments) <= 1 { if len(splittedComments) <= 1 {
return errors.New("Can't deserialize Object, there's no commentary") return errors.New("Can't deserialize Object, there's no commentary")
@@ -256,10 +283,19 @@ func (d *Workflow) getNewGraphItem(dataName string, graphItem *graph.GraphItem,
d.Datas = append(d.Datas, resource.GetID()) d.Datas = append(d.Datas, resource.GetID())
d.DataResources = append(d.DataResources, resource.(*resources.DataResource)) d.DataResources = append(d.DataResources, resource.(*resources.DataResource))
graphItem.Data = resource.(*resources.DataResource) graphItem.Data = resource.(*resources.DataResource)
case "Processing", "Event": case "Processing":
d.Processings = append(d.Processings, resource.GetID()) d.Processings = append(d.Processings, resource.GetID())
d.ProcessingResources = append(d.ProcessingResources, resource.(*resources.ProcessingResource)) d.ProcessingResources = append(d.ProcessingResources, resource.(*resources.ProcessingResource))
graphItem.Processing = resource.(*resources.ProcessingResource) graphItem.Processing = resource.(*resources.ProcessingResource)
case "Event":
access := resources.NewAccessor[*resources.NativeTool](tools.NATIVE_TOOL, &tools.APIRequest{
Admin: true,
}, func() utils.DBObject { return &resources.NativeTool{} })
t, _, err := access.Search(nil, "WORKFLOW_EVENT", false)
if err == nil && len(t) > 0 {
d.NativeTool = append(d.NativeTool, t[0].GetID())
graphItem.NativeTool = t[0].(*resources.NativeTool)
}
case "Storage": case "Storage":
d.Storages = append(d.Storages, resource.GetID()) d.Storages = append(d.Storages, resource.GetID())
d.StorageResources = append(d.StorageResources, resource.(*resources.StorageResource)) d.StorageResources = append(d.StorageResources, resource.(*resources.StorageResource))
@@ -312,6 +348,12 @@ func (w *Workflow) IsDependancy(id string) []Deps {
return dependancyOfIDs return dependancyOfIDs
} }
func (w *Workflow) GetFirstItems() []graph.GraphItem {
return w.GetGraphItems(func(item graph.GraphItem) bool {
return len(w.GetDependencies(w.GetID())) == 0
})
}
func (w *Workflow) GetDependencies(id string) (dependencies []Deps) { func (w *Workflow) GetDependencies(id string) (dependencies []Deps) {
for _, link := range w.Graph.Links { for _, link := range w.Graph.Links {
if _, ok := w.Graph.Items[link.Source.ID]; !ok { if _, ok := w.Graph.Items[link.Source.ID]; !ok {
@@ -336,16 +378,26 @@ func (w *Workflow) GetGraphItems(f func(item graph.GraphItem) bool) (list_datas
} }
func (w *Workflow) GetPricedItem( func (w *Workflow) GetPricedItem(
f func(item graph.GraphItem) bool, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) map[string]pricing.PricedItemITF { f func(item graph.GraphItem) bool, request *tools.APIRequest,
instance int,
partnership int,
buying int,
strategy int,
bookingMode int,
buyingStrategy int,
pricingStrategy int) (map[string]pricing.PricedItemITF, error) {
list_datas := map[string]pricing.PricedItemITF{} list_datas := map[string]pricing.PricedItemITF{}
for _, item := range w.Graph.Items { for _, item := range w.Graph.Items {
if f(item) { if f(item) {
dt, res := item.GetResource() dt, res := item.GetResource()
ord := res.ConvertToPricedResource(dt, request) ord, err := res.ConvertToPricedResource(dt, &instance, &partnership, &buying, &strategy, &bookingMode, request)
if err != nil {
return list_datas, err
}
list_datas[res.GetID()] = ord list_datas[res.GetID()] = ord
} }
} }
return list_datas return list_datas, nil
} }
type Related struct { type Related struct {
@@ -378,7 +430,7 @@ func (w *Workflow) GetByRelatedProcessing(processingID string, g func(item graph
return related return related
} }
func (ao *Workflow) VerifyAuth(request *tools.APIRequest) bool { func (ao *Workflow) VerifyAuth(callName string, request *tools.APIRequest) bool {
isAuthorized := false isAuthorized := false
if len(ao.Shared) > 0 { if len(ao.Shared) > 0 {
for _, shared := range ao.Shared { for _, shared := range ao.Shared {
@@ -386,11 +438,11 @@ func (ao *Workflow) VerifyAuth(request *tools.APIRequest) bool {
if code != 200 || shared == nil { if code != 200 || shared == nil {
isAuthorized = false isAuthorized = false
} else { } else {
isAuthorized = shared.VerifyAuth(request) isAuthorized = shared.VerifyAuth(callName, request)
} }
} }
} }
return ao.AbstractObject.VerifyAuth(request) || isAuthorized return ao.AbstractObject.VerifyAuth(callName, request) || isAuthorized
} }
/* /*
@@ -422,65 +474,106 @@ func (wfa *Workflow) CheckBooking(caller *tools.HTTPCaller) (bool, error) {
return true, nil return true, nil
} }
func (wf *Workflow) Planify(start time.Time, end *time.Time, request *tools.APIRequest) (float64, map[tools.DataType]map[string]pricing.PricedItemITF, *Workflow, error) { func (wf *Workflow) Planify(start time.Time, end *time.Time, instances ConfigItem, partnerships ConfigItem, buyings ConfigItem, strategies ConfigItem, bookingMode int, request *tools.APIRequest) (bool, float64, map[tools.DataType]map[string]pricing.PricedItemITF, *Workflow, error) {
priceds := map[tools.DataType]map[string]pricing.PricedItemITF{} priceds := map[tools.DataType]map[string]pricing.PricedItemITF{}
ps, priceds, err := plan[*resources.ProcessingResource](tools.PROCESSING_RESOURCE, wf, priceds, request, wf.Graph.IsProcessing, ps, priceds, err := plan[*resources.ProcessingResource](tools.PROCESSING_RESOURCE, instances, partnerships, buyings, strategies, bookingMode, wf, priceds, request, wf.Graph.IsProcessing,
func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) { func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64, error) {
return start.Add(time.Duration(wf.Graph.GetAverageTimeProcessingBeforeStart(0, res.GetID(), request)) * time.Second), priced.GetExplicitDurationInS() d, err := wf.Graph.GetAverageTimeProcessingBeforeStart(0, res.GetID(),
}, func(started time.Time, duration float64) *time.Time { *instances.Get(res.GetID()), *partnerships.Get(res.GetID()), *buyings.Get(res.GetID()), *strategies.Get(res.GetID()),
bookingMode, request)
if err != nil {
return start, 0, err
}
return start.Add(time.Duration(d) * time.Second), priced.GetExplicitDurationInS(), nil
}, func(started time.Time, duration float64) (*time.Time, error) {
s := started.Add(time.Duration(duration)) s := started.Add(time.Duration(duration))
return &s return &s, nil
}) })
if err != nil { if err != nil {
return 0, priceds, nil, err return false, 0, priceds, nil, err
} }
if _, priceds, err = plan[resources.ResourceInterface](tools.DATA_RESOURCE, wf, priceds, request, if _, priceds, err = plan[resources.ResourceInterface](tools.NATIVE_TOOL, instances, partnerships, buyings, strategies, bookingMode, wf, priceds, request,
wf.Graph.IsData, func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) { wf.Graph.IsNativeTool, func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64, error) {
return start, 0 return start, 0, nil
}, func(started time.Time, duration float64) *time.Time { }, func(started time.Time, duration float64) (*time.Time, error) {
return end return end, nil
}); err != nil { }); err != nil {
return 0, priceds, nil, err return false, 0, priceds, nil, err
} }
for k, f := range map[tools.DataType]func(graph.GraphItem) bool{tools.STORAGE_RESOURCE: wf.Graph.IsStorage, tools.COMPUTE_RESOURCE: wf.Graph.IsCompute} { if _, priceds, err = plan[resources.ResourceInterface](tools.DATA_RESOURCE, instances, partnerships, buyings, strategies, bookingMode, wf, priceds, request,
if _, priceds, err = plan[resources.ResourceInterface](k, wf, priceds, request, wf.Graph.IsData, func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64, error) {
f, func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) { return start, 0, nil
nearestStart, longestDuration := wf.Graph.GetAverageTimeRelatedToProcessingActivity(start, ps, res, func(i graph.GraphItem) (r resources.ResourceInterface) { }, func(started time.Time, duration float64) (*time.Time, error) {
return end, nil
}); err != nil {
return false, 0, priceds, nil, err
}
for k, f := range map[tools.DataType]func(graph.GraphItem) bool{tools.STORAGE_RESOURCE: wf.Graph.IsStorage,
tools.COMPUTE_RESOURCE: wf.Graph.IsCompute} {
if _, priceds, err = plan[resources.ResourceInterface](k, instances, partnerships, buyings, strategies, bookingMode, wf, priceds, request,
f, func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64, error) {
nearestStart, longestDuration, err := wf.Graph.GetAverageTimeRelatedToProcessingActivity(start, ps, res, func(i graph.GraphItem) (r resources.ResourceInterface) {
if f(i) { if f(i) {
_, r = i.GetResource() _, r = i.GetResource()
} }
return r return r
}, request) }, *instances.Get(res.GetID()), *partnerships.Get(res.GetID()),
return start.Add(time.Duration(nearestStart) * time.Second), longestDuration *buyings.Get(res.GetID()), *strategies.Get(res.GetID()), bookingMode, request)
}, func(started time.Time, duration float64) *time.Time { if err != nil {
return start, longestDuration, err
}
return start.Add(time.Duration(nearestStart) * time.Second), longestDuration, nil
}, func(started time.Time, duration float64) (*time.Time, error) {
s := started.Add(time.Duration(duration)) s := started.Add(time.Duration(duration))
return &s return &s, nil
}); err != nil { }); err != nil {
return 0, priceds, nil, err return false, 0, priceds, nil, err
} }
} }
longest := common.GetPlannerLongestTime(end, priceds, request) longest := common.GetPlannerLongestTime(end, priceds, request)
if _, priceds, err = plan[resources.ResourceInterface](tools.WORKFLOW_RESOURCE, wf, priceds, request, wf.Graph.IsWorkflow, if _, priceds, err = plan[resources.ResourceInterface](tools.WORKFLOW_RESOURCE, instances, partnerships, buyings, strategies,
func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) { bookingMode, wf, priceds, request, wf.Graph.IsWorkflow,
func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64, error) {
start := start.Add(time.Duration(common.GetPlannerNearestStart(start, priceds, request)) * time.Second) start := start.Add(time.Duration(common.GetPlannerNearestStart(start, priceds, request)) * time.Second)
longest := float64(-1) longest := float64(-1)
r, code, err := res.GetAccessor(request).LoadOne(res.GetID()) r, code, err := res.GetAccessor(request).LoadOne(res.GetID())
if code != 200 || err != nil { if code != 200 || err != nil {
return start, longest return start, longest, err
} }
if neoLongest, _, _, err := r.(*Workflow).Planify(start, end, request); err != nil { _, neoLongest, priceds2, _, err := r.(*Workflow).Planify(start, end, instances, partnerships, buyings, strategies, bookingMode, request)
return start, longest // should ... import priced
if err != nil {
return start, longest, err
} else if neoLongest > longest { } else if neoLongest > longest {
longest = neoLongest longest = neoLongest
} }
return start.Add(time.Duration(common.GetPlannerNearestStart(start, priceds, request)) * time.Second), longest for k, v := range priceds2 {
}, func(start time.Time, longest float64) *time.Time { if priceds[k] == nil {
priceds[k] = map[string]pricing.PricedItemITF{}
}
for k2, v2 := range v {
if priceds[k][k2] != nil {
v2.AddQuantity(priceds[k][k2].GetQuantity())
}
}
}
return start.Add(time.Duration(common.GetPlannerNearestStart(start, priceds, request)) * time.Second), longest, nil
}, func(start time.Time, longest float64) (*time.Time, error) {
s := start.Add(time.Duration(longest) * time.Second) s := start.Add(time.Duration(longest) * time.Second)
return &s return &s, nil
}); err != nil { }); err != nil {
return 0, priceds, nil, err return false, 0, priceds, nil, err
} }
return longest, priceds, wf, nil isPreemptible := true
for _, first := range wf.GetFirstItems() {
_, res := first.GetResource()
if res.GetBookingModes()[booking.PREEMPTED] == nil {
isPreemptible = false
break
}
}
return isPreemptible, longest, priceds, wf, nil
} }
// Returns a map of DataType (processing,computing,data,storage,worfklow) where each resource (identified by its UUID) // Returns a map of DataType (processing,computing,data,storage,worfklow) where each resource (identified by its UUID)
@@ -511,8 +604,10 @@ func (w *Workflow) GetItemsByResources() map[tools.DataType]map[string][]string
} }
func plan[T resources.ResourceInterface]( func plan[T resources.ResourceInterface](
dt tools.DataType, wf *Workflow, priceds map[tools.DataType]map[string]pricing.PricedItemITF, request *tools.APIRequest, dt tools.DataType, instances ConfigItem, partnerships ConfigItem, buyings ConfigItem, strategies ConfigItem, bookingMode int, wf *Workflow, priceds map[tools.DataType]map[string]pricing.PricedItemITF, request *tools.APIRequest,
f func(graph.GraphItem) bool, start func(resources.ResourceInterface, pricing.PricedItemITF) (time.Time, float64), end func(time.Time, float64) *time.Time) ([]T, map[tools.DataType]map[string]pricing.PricedItemITF, error) { f func(graph.GraphItem) bool,
start func(resources.ResourceInterface, pricing.PricedItemITF) (time.Time, float64, error),
end func(time.Time, float64) (*time.Time, error)) ([]T, map[tools.DataType]map[string]pricing.PricedItemITF, error) {
resources := []T{} resources := []T{}
for _, item := range wf.GetGraphItems(f) { for _, item := range wf.GetGraphItems(f) {
if priceds[dt] == nil { if priceds[dt] == nil {
@@ -522,23 +617,34 @@ func plan[T resources.ResourceInterface](
if realItem == nil { if realItem == nil {
return resources, priceds, errors.New("could not load the processing resource") return resources, priceds, errors.New("could not load the processing resource")
} }
priced := realItem.ConvertToPricedResource(dt, request) priced, err := realItem.ConvertToPricedResource(dt, instances.Get(realItem.GetID()),
partnerships.Get(realItem.GetID()), buyings.Get(realItem.GetID()), strategies.Get(realItem.GetID()), &bookingMode, request)
if err != nil {
return resources, priceds, err
}
// Should be commented once the Pricing selection feature has been implemented, related to the commit d35ad440fa77763ec7f49ab34a85e47e75581b61 // Should be commented once the Pricing selection feature has been implemented, related to the commit d35ad440fa77763ec7f49ab34a85e47e75581b61
// if priced.SelectPricing() == nil { // if priced.SelectPricing() == nil {
// return resources, priceds, errors.New("no pricings are selected... can't proceed") // return resources, priceds, errors.New("no pricings are selected... can't proceed")
// } // }
started, duration := start(realItem, priced) started, duration, err := start(realItem, priced)
if err != nil {
return resources, priceds, err
}
priced.SetLocationStart(started) priced.SetLocationStart(started)
if duration >= 0 { if duration >= 0 {
if e := end(started, duration); e != nil { if e, err := end(started, duration); err == nil && e != nil {
priced.SetLocationEnd(*e) priced.SetLocationEnd(*e)
} }
} }
if e := end(started, priced.GetExplicitDurationInS()); e != nil { if e, err := end(started, priced.GetExplicitDurationInS()); err != nil && e != nil {
priced.SetLocationEnd(*e) priced.SetLocationEnd(*e)
} }
resources = append(resources, realItem.(T)) resources = append(resources, realItem.(T))
if priceds[dt][item.ID] != nil {
priced.AddQuantity(priceds[dt][item.ID].GetQuantity())
}
priceds[dt][item.ID] = priced priceds[dt][item.ID] = priced
} }
return resources, priceds, nil return resources, priceds, nil
} }

View File

@@ -29,7 +29,7 @@ func TestNewScheduler_ValidInput(t *testing.T) {
dur := 7200.0 dur := 7200.0
cronStr := "0 0 * * * *" cronStr := "0 0 * * * *"
sched := workflow_execution.NewScheduler(s, e, dur, cronStr) sched := workflow_execution.NewScheduler(0, s, e, dur, cronStr)
assert.NotNil(t, sched) assert.NotNil(t, sched)
assert.Equal(t, dur, sched.DurationS) assert.Equal(t, dur, sched.DurationS)
@@ -42,7 +42,7 @@ func TestNewScheduler_InvalidStart(t *testing.T) {
dur := 7200.0 dur := 7200.0
cronStr := "0 0 * * * *" cronStr := "0 0 * * * *"
sched := workflow_execution.NewScheduler(s, e, dur, cronStr) sched := workflow_execution.NewScheduler(0, s, e, dur, cronStr)
assert.Nil(t, sched) assert.Nil(t, sched)
} }
@@ -52,7 +52,7 @@ func TestNewScheduler_InvalidEnd(t *testing.T) {
dur := 7200.0 dur := 7200.0
cronStr := "0 0 * * * *" cronStr := "0 0 * * * *"
sched := workflow_execution.NewScheduler(s, e, dur, cronStr) sched := workflow_execution.NewScheduler(0, s, e, dur, cronStr)
assert.NotNil(t, sched) assert.NotNil(t, sched)
assert.Nil(t, sched.End) assert.Nil(t, sched.End)
} }
@@ -120,7 +120,7 @@ func TestGetExecutions_Success(t *testing.T) {
}, },
} }
execs, err := ws.GetExecutions(wf) execs, err := ws.GetExecutions(wf, false)
assert.NoError(t, err) assert.NoError(t, err)
assert.Greater(t, len(execs), 0) assert.Greater(t, len(execs), 0)
assert.Equal(t, wf.UUID, execs[0].WorkflowID) assert.Equal(t, wf.UUID, execs[0].WorkflowID)

View File

@@ -93,7 +93,7 @@ func TestGetName_ReturnsCorrectFormat(t *testing.T) {
func TestVerifyAuth_AlwaysTrue(t *testing.T) { func TestVerifyAuth_AlwaysTrue(t *testing.T) {
exec := &workflow_execution.WorkflowExecution{} exec := &workflow_execution.WorkflowExecution{}
assert.True(t, exec.VerifyAuth(nil)) assert.True(t, exec.VerifyAuth("get", nil))
} }
func TestUpdateOne_RejectsZeroState(t *testing.T) { func TestUpdateOne_RejectsZeroState(t *testing.T) {

View File

@@ -23,6 +23,7 @@ import (
*/ */
type WorkflowExecution struct { type WorkflowExecution struct {
utils.AbstractObject // AbstractObject contains the basic fields of an object (id, name) utils.AbstractObject // AbstractObject contains the basic fields of an object (id, name)
Priority int `json:"priority" bson:"priority"` // will best effort on default // Will Best Effort on priority
PeerBuyByGraph map[string]map[string][]string `json:"peer_buy_by_graph,omitempty" bson:"peer_buy_by_graph,omitempty"` // BookByResource is a map of the resource id and the list of the booking id PeerBuyByGraph map[string]map[string][]string `json:"peer_buy_by_graph,omitempty" bson:"peer_buy_by_graph,omitempty"` // BookByResource is a map of the resource id and the list of the booking id
PeerBookByGraph map[string]map[string][]string `json:"peer_book_by_graph,omitempty" bson:"peer_book_by_graph,omitempty"` // BookByResource is a map of the resource id and the list of the booking id PeerBookByGraph map[string]map[string][]string `json:"peer_book_by_graph,omitempty" bson:"peer_book_by_graph,omitempty"` // BookByResource is a map of the resource id and the list of the booking id
ExecutionsID string `json:"executions_id,omitempty" bson:"executions_id,omitempty"` ExecutionsID string `json:"executions_id,omitempty" bson:"executions_id,omitempty"`
@@ -108,7 +109,7 @@ func (d *WorkflowExecution) GetAccessor(request *tools.APIRequest) utils.Accesso
return NewAccessor(request) // Create a new instance of the accessor return NewAccessor(request) // Create a new instance of the accessor
} }
func (d *WorkflowExecution) VerifyAuth(request *tools.APIRequest) bool { func (d *WorkflowExecution) VerifyAuth(callName string, request *tools.APIRequest) bool {
return true return true
} }

View File

@@ -37,20 +37,42 @@ type WorkflowSchedule struct {
DurationS float64 `json:"duration_s" default:"-1"` // End is the end time of the schedule DurationS float64 `json:"duration_s" default:"-1"` // End is the end time of the schedule
Cron string `json:"cron,omitempty"` // here the cron format : ss mm hh dd MM dw task Cron string `json:"cron,omitempty"` // here the cron format : ss mm hh dd MM dw task
BookingMode booking.BookingMode `json:"booking_mode,omitempty"` // BookingMode qualify the preemption order of the scheduling. if no payment allowed with preemption set up When_Possible
SelectedInstances workflow.ConfigItem `json:"selected_instances"`
SelectedPartnerships workflow.ConfigItem `json:"selected_partnerships"`
SelectedBuyings workflow.ConfigItem `json:"selected_buyings"`
SelectedStrategies workflow.ConfigItem `json:"selected_strategies"`
SelectedBillingStrategy pricing.BillingStrategy `json:"selected_billing_strategy"` SelectedBillingStrategy pricing.BillingStrategy `json:"selected_billing_strategy"`
} }
func NewScheduler(start string, end string, durationInS float64, cron string) *WorkflowSchedule { // TODO PREEMPTION !
s, err := time.Parse("2006-01-02T15:04:05", start) /*
if err != nil { To schedule a preempted, omg.
return nil pour faire ça on doit alors lancé une exécution prioritaire qui passera devant toutes les autres, celon un niveau de priorité.
} Preemptible = 7, pour le moment il n'existera que 0 et 7.
Dans le cas d'une préemption l'exécution est immédiable et bloquera tout le monde tant qu'il n'a pas été exécuté.
Une ressource doit pouvoir être preemptible pour être exécutée de la sorte.
Se qui implique si on est sur une ressource par ressource que si un élement n'est pas préemptible,
alors il devra être effectué dés que possible
Dans le cas dés que possible, la start date est immédiate MAIS !
ne pourra se lancé que SI il n'existe pas d'exécution se lançant durant la période indicative. ( Ultra complexe )
*/
func NewScheduler(mode int, start string, end string, durationInS float64, cron string) *WorkflowSchedule {
ws := &WorkflowSchedule{ ws := &WorkflowSchedule{
UUID: uuid.New().String(), UUID: uuid.New().String(),
Start: s, Start: time.Now(),
DurationS: durationInS, BookingMode: booking.BookingMode(mode),
Cron: cron, DurationS: durationInS,
Cron: cron,
} }
s, err := time.Parse("2006-01-02T15:04:05", start)
if err == nil && ws.BookingMode == booking.PLANNED {
ws.Start = s // can apply a defined start other than now, if planned
}
e, err := time.Parse("2006-01-02T15:04:05", end) e, err := time.Parse("2006-01-02T15:04:05", end)
if err == nil { if err == nil {
ws.End = &e ws.End = &e
@@ -68,7 +90,9 @@ func (ws *WorkflowSchedule) GetBuyAndBook(wfID string, request *tools.APIRequest
return false, nil, []*WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, errors.New("could not load the workflow with id: " + err.Error()) return false, nil, []*WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, errors.New("could not load the workflow with id: " + err.Error())
} }
wf := res.(*workflow.Workflow) wf := res.(*workflow.Workflow)
longest, priceds, wf, err := wf.Planify(ws.Start, ws.End, request) isPreemptible, longest, priceds, wf, err := wf.Planify(ws.Start, ws.End,
ws.SelectedInstances, ws.SelectedPartnerships, ws.SelectedBuyings, ws.SelectedStrategies,
int(ws.BookingMode), request)
if err != nil { if err != nil {
return false, wf, []*WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, err return false, wf, []*WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, err
} }
@@ -77,7 +101,7 @@ func (ws *WorkflowSchedule) GetBuyAndBook(wfID string, request *tools.APIRequest
if ws.End != nil && ws.Start.Add(time.Duration(longest)*time.Second).After(*ws.End) { if ws.End != nil && ws.Start.Add(time.Duration(longest)*time.Second).After(*ws.End) {
ws.Warning = "The workflow may be too long to be executed in the given time frame, we will try to book it anyway\n" ws.Warning = "The workflow may be too long to be executed in the given time frame, we will try to book it anyway\n"
} }
execs, err := ws.GetExecutions(wf) execs, err := ws.GetExecutions(wf, isPreemptible)
if err != nil { if err != nil {
return false, wf, []*WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, err return false, wf, []*WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, err
} }
@@ -250,7 +274,7 @@ VERIFY THAT WE HANDLE DIFFERENCE BETWEEN LOCATION TIME && BOOKING
* getExecutions is a function that returns the executions of a workflow * getExecutions is a function that returns the executions of a workflow
* it returns an array of workflow_execution.WorkflowExecution * it returns an array of workflow_execution.WorkflowExecution
*/ */
func (ws *WorkflowSchedule) GetExecutions(workflow *workflow.Workflow) ([]*WorkflowExecution, error) { func (ws *WorkflowSchedule) GetExecutions(workflow *workflow.Workflow, isPreemptible bool) ([]*WorkflowExecution, error) {
workflows_executions := []*WorkflowExecution{} workflows_executions := []*WorkflowExecution{}
dates, err := ws.GetDates() dates, err := ws.GetDates()
if err != nil { if err != nil {
@@ -262,12 +286,19 @@ func (ws *WorkflowSchedule) GetExecutions(workflow *workflow.Workflow) ([]*Workf
UUID: uuid.New().String(), // set the uuid of the execution UUID: uuid.New().String(), // set the uuid of the execution
Name: workflow.Name + "_execution_" + date.Start.String(), // set the name of the execution Name: workflow.Name + "_execution_" + date.Start.String(), // set the name of the execution
}, },
Priority: 1,
ExecutionsID: ws.UUID, ExecutionsID: ws.UUID,
ExecDate: date.Start, // set the execution date ExecDate: date.Start, // set the execution date
EndDate: date.End, // set the end date EndDate: date.End, // set the end date
State: enum.DRAFT, // set the state to 1 (scheduled) State: enum.DRAFT, // set the state to 1 (scheduled)
WorkflowID: workflow.GetID(), // set the workflow id dependancy of the execution WorkflowID: workflow.GetID(), // set the workflow id dependancy of the execution
} }
if ws.BookingMode != booking.PLANNED {
obj.Priority = 0
}
if ws.BookingMode == booking.PREEMPTED && isPreemptible {
obj.Priority = 7
}
workflows_executions = append(workflows_executions, obj) workflows_executions = append(workflows_executions, obj)
} }
return workflows_executions, nil return workflows_executions, nil

View File

@@ -20,13 +20,13 @@ func (d *Workspace) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor(request) // Create a new instance of the accessor return NewAccessor(request) // Create a new instance of the accessor
} }
func (ao *Workspace) VerifyAuth(request *tools.APIRequest) bool { func (ao *Workspace) VerifyAuth(callName string, request *tools.APIRequest) bool {
if ao.Shared != "" { if ao.Shared != "" {
shared, code, _ := shallow_collaborative_area.NewAccessor(request).LoadOne(ao.Shared) shared, code, _ := shallow_collaborative_area.NewAccessor(request).LoadOne(ao.Shared)
if code != 200 || shared == nil { if code != 200 || shared == nil {
return false return false
} }
return shared.VerifyAuth(request) return shared.VerifyAuth(callName, request)
} }
return ao.AbstractObject.VerifyAuth(request) return ao.AbstractObject.VerifyAuth(callName, request)
} }

View File

@@ -16,6 +16,7 @@ type APIRequest struct {
PeerID string PeerID string
Groups []string Groups []string
Caller *HTTPCaller Caller *HTTPCaller
Admin bool
} }
/* /*

View File

@@ -31,6 +31,7 @@ const (
BILL BILL
MINIO_SVCACC MINIO_SVCACC
MINIO_SVCACC_SECRET MINIO_SVCACC_SECRET
NATIVE_TOOL
) )
var NOAPI = "" var NOAPI = ""
@@ -77,6 +78,7 @@ var DefaultAPI = [...]string{
NOAPI, NOAPI,
MINIO, MINIO,
MINIO, MINIO,
CATALOGAPI,
} }
// Bind the standard data name to the data type // Bind the standard data name to the data type
@@ -108,6 +110,7 @@ var Str = [...]string{
"bill", "bill",
"service_account", "service_account",
"secret", "secret",
"native_tool",
} }
func FromInt(i int) string { func FromInt(i int) string {
@@ -131,5 +134,5 @@ func DataTypeList() []DataType {
return []DataType{DATA_RESOURCE, PROCESSING_RESOURCE, STORAGE_RESOURCE, COMPUTE_RESOURCE, WORKFLOW_RESOURCE, return []DataType{DATA_RESOURCE, PROCESSING_RESOURCE, STORAGE_RESOURCE, COMPUTE_RESOURCE, WORKFLOW_RESOURCE,
WORKFLOW, WORKFLOW_EXECUTION, WORKSPACE, PEER, COLLABORATIVE_AREA, RULE, BOOKING, WORKFLOW_HISTORY, WORKSPACE_HISTORY, WORKFLOW, WORKFLOW_EXECUTION, WORKSPACE, PEER, COLLABORATIVE_AREA, RULE, BOOKING, WORKFLOW_HISTORY, WORKSPACE_HISTORY,
ORDER, PURCHASE_RESOURCE, ADMIRALTY_SOURCE, ADMIRALTY_TARGET, ADMIRALTY_SECRET, ADMIRALTY_KUBECONFIG, ADMIRALTY_NODES, ORDER, PURCHASE_RESOURCE, ADMIRALTY_SOURCE, ADMIRALTY_TARGET, ADMIRALTY_SECRET, ADMIRALTY_KUBECONFIG, ADMIRALTY_NODES,
LIVE_DATACENTER, LIVE_STORAGE, BILL} LIVE_DATACENTER, LIVE_STORAGE, BILL, NATIVE_TOOL}
} }