diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 9a23dc6..0000000 --- a/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# Build the manager binary -FROM golang:1.17 as builder - -WORKDIR /workspace -# Copy the Go Modules manifests -COPY go.mod go.mod -COPY go.sum go.sum -# cache deps before building and copying source so that we don't need to re-download as much -# and so that source changes don't invalidate our downloaded layer -RUN go mod download - -# Copy the go source -COPY main.go main.go -COPY apis/ apis/ -COPY controllers/ controllers/ - -# Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go - -# Use distroless as minimal base image to package the manager binary -# Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM gcr.io/distroless/static:nonroot -WORKDIR / -COPY --from=builder /workspace/manager . -USER 65532:65532 - -ENTRYPOINT ["/manager"] diff --git a/log.txt b/log.txt deleted file mode 100644 index 3f2a22a..0000000 --- a/log.txt +++ /dev/null @@ -1,5235 +0,0 @@ -/Users/alessandro/go/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./apis/..." -rm -f deployments/node/files/* -/Users/alessandro/go/bin/controller-gen paths="./pkg/local-resource-manager" rbac:roleName=node-local-resource-manager output:rbac:stdout | awk -v RS="---\n" 'NR>1{f="./deployments/node/files/node-local-resource-manager-" $4 ".yaml";printf "%s",$0 > f; close(f)}' && sed -i '' -n '/rules/,$p' deployments/node/files/node-local-resource-manager-ClusterRole.yaml -/Users/alessandro/go/bin/controller-gen paths="./pkg/rear-manager/" rbac:roleName=node-rear-manager output:rbac:stdout | awk -v RS="---\n" 'NR>1{f="./deployments/node/files/node-rear-manager-" $4 ".yaml";printf "%s",$0 > f; close(f)}' && sed -i '' -n '/rules/,$p' deployments/node/files/node-rear-manager-ClusterRole.yaml -/Users/alessandro/go/bin/controller-gen paths="./pkg/rear-controller/..." rbac:roleName=node-rear-controller output:rbac:stdout | awk -v RS="---\n" 'NR>1{f="./deployments/node/files/node-rear-controller-" $4 ".yaml";printf "%s",$0 > f; close(f)}' && sed -i '' -n '/rules/,$p' deployments/node/files/node-rear-controller-ClusterRole.yaml -rm -f deployments/node/crds/* -/Users/alessandro/go/bin/controller-gen paths="./apis/..." crd:generateEmbeddedObjectMeta=true output:crd:artifacts:config=deployments/node/crds -go mod tidy -go fmt ./... -find . -type f -name '*.go' -a ! -name '*zz_generated*' -exec /Users/alessandro/go/bin/gci write -s standard -s default -s "prefix(github.com/fluidos-project/node)" {} \; -/* -Copyright 2023. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "context" - "flag" - "os" - - advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - contractmanager "github.com/fluidos-project/node/pkg/rear-controller/contract-manager" - discoverymanager "github.com/fluidos-project/node/pkg/rear-controller/discovery-manager" - gateway "github.com/fluidos-project/node/pkg/rear-controller/gateway" - "github.com/fluidos-project/node/pkg/rear-controller/grpc" - "github.com/fluidos-project/node/pkg/utils/flags" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - - // to ensure that exec-entrypoint and run can make use of them. - _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") -) - -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(advertisementv1alpha1.AddToScheme(scheme)) - utilruntime.Must(reservationv1alpha1.AddToScheme(scheme)) - utilruntime.Must(nodecorev1alpha1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme -} - -func main() { - var metricsAddr string - var enableLeaderElection bool - var probeAddr string - flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") - flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") - flag.StringVar(&flags.GRPC_PORT, "grpc-port", "2710", "Port of the HTTP server") - flag.StringVar(&flags.HTTP_PORT, "http-port", "3004", "Port of the HTTP server") - flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.") - opts := zap.Options{ - Development: true, - } - opts.BindFlags(flag.CommandLine) - flag.Parse() - - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) - - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, - HealthProbeBindAddress: probeAddr, - LeaderElection: enableLeaderElection, - LeaderElectionID: "efa8b828.fluidos.eu", - }) - if err != nil { - setupLog.Error(err, "unable to start manager") - os.Exit(1) - } - - cache := mgr.GetCache() - - // Index the TransactionID field of the Contract CRD - indexFuncTransaction := func(obj client.Object) []string { - contract := obj.(*reservationv1alpha1.Contract) - return []string{contract.Spec.TransactionID} - } - - // Index the ClusterID field of the Contract CRD - indexFuncClusterID := func(obj client.Object) []string { - contract := obj.(*reservationv1alpha1.Contract) - return []string{contract.Spec.BuyerClusterID} - } - - if err := cache.IndexField(context.Background(), &reservationv1alpha1.Contract{}, "spec.transactionID", indexFuncTransaction); err != nil { - setupLog.Error(err, "unable to create index for field", "field", "spec.transactionID") - os.Exit(1) - } - - if err := cache.IndexField(context.Background(), &reservationv1alpha1.Contract{}, "spec.buyerClusterID", indexFuncClusterID); err != nil { - setupLog.Error(err, "unable to create index for field", "field", "spec.buyerClusterID") - os.Exit(1) - } - - gw := gateway.NewGateway(mgr.GetClient()) - grpcServer := grpc.NewGrpcServer(mgr.GetClient()) - - if err = (&discoverymanager.DiscoveryReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Gateway: gw, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Discovery") - os.Exit(1) - } - - if err = (&contractmanager.ReservationReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Gateway: gw, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Reservation") - os.Exit(1) - } - //+kubebuilder:scaffold:builder - - if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up health check") - os.Exit(1) - } - if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up ready check") - os.Exit(1) - } - - // Periodically clear the transaction cache - if err := mgr.Add(manager.RunnableFunc(gw.CacheRefresher(flags.REFRESH_CACHE_INTERVAL))); err != nil { - klog.Errorf("Unable to set up transaction cache refresher: %s", err) - os.Exit(1) - } - - // Periodically check if Liqo is ready - if err := mgr.Add(manager.RunnableFunc(gw.LiqoChecker(flags.LIQO_CHECK_INTERVAL))); err != nil { - klog.Errorf("Unable to set up Liqo checker: %s", err) - os.Exit(1) - } - - // Start the REAR Gateway HTTP server - if err := mgr.Add(manager.RunnableFunc(gw.Start)); err != nil { - klog.Errorf("Unable to set up Gateway HTTP server: %s", err) - os.Exit(1) - } - - // Start the REAR GRPC server - if err := mgr.Add(manager.RunnableFunc(grpcServer.Start)); err != nil { - klog.Errorf("Unable to set up Gateway GRPC server: %s", err) - os.Exit(1) - } - - // Start the REAR Gateway HTTP server - /* go func() { - gw.Start() - }() */ - - // Start the REAR GRPC server - /* go func() { - grpcServer.Start() - }() */ - - // TODO: Uncomment this when the webhook is ready. For now it does not work (Ale) - // pcv := discoverymanager.NewPCValidator(mgr.GetClient()) - - // mgr.GetWebhookServer().Register("/validate/peeringcandidate", &webhook.Admission{Handler: pcv}) - - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) - } -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "flag" - "net/http" - "os" - - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - localResourceManager "github.com/fluidos-project/node/pkg/local-resource-manager" - "github.com/fluidos-project/node/pkg/utils/flags" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - metricsv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") -) - -func init() { - utilruntime.Must(metricsv1beta1.AddToScheme(scheme)) - utilruntime.Must(corev1.AddToScheme(scheme)) - utilruntime.Must(nodecorev1alpha1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme -} - -func main() { - var probeAddr string - flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") - flag.StringVar(&flags.AMOUNT, "amount", "", "Amount of money set for the flavours of this node") - flag.StringVar(&flags.CURRENCY, "currency", "", "Currency of the money set for the flavours of this node") - flag.StringVar(&flags.PERIOD, "period", "", "Period set for the flavours of this node") - flag.StringVar(&flags.RESOURCE_TYPE, "resources-types", "k8s-fluidos", "Type of the Flavour related to k8s resources") - flag.StringVar(&flags.CPU_MIN, "cpu-min", "0", "Minimum CPU value") - flag.StringVar(&flags.MEMORY_MIN, "memory-min", "0", "Minimum memory value") - flag.StringVar(&flags.CPU_STEP, "cpu-step", "0", "CPU step value") - flag.StringVar(&flags.MEMORY_STEP, "memory-step", "0", "Memory step value") - flag.Int64Var(&flags.MIN_COUNT, "min-count", 0, "Minimum number of flavours") - flag.Int64Var(&flags.MAX_COUNT, "max-count", 0, "Maximum number of flavours") - flag.StringVar(&flags.RESOURCE_NODE_LABEL, "node-resource-label", "node-role.fluidos.eu/resources", "Label used to filter the k8s nodes from which create flavours") - - flag.Parse() - - cfg := ctrl.GetConfigOrDie() - cl, err := client.New(cfg, client.Options{Scheme: scheme}) - if err != nil { - setupLog.Error(err, "Unable to create client") - os.Exit(1) - } - - err = localResourceManager.Start(context.Background(), cl) - if err != nil { - setupLog.Error(err, "Unable to start LocalResourceManager") - os.Exit(1) - } - - mux := http.NewServeMux() - mux.HandleFunc("/healthz", healthHandler) // health check endpoint - mux.HandleFunc("/readyz", healthHandler) // readiness check endpoint - - server := &http.Server{ - Addr: probeAddr, - Handler: mux, - } - - setupLog.Info("Starting server", "address", probeAddr) - if err := server.ListenAndServe(); err != http.ErrServerClosed { - setupLog.Error(err, "Server stopped unexpectedly") - os.Exit(1) - } -} - -func healthHandler(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte("OK")) -} -/* -Copyright 2023. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "flag" - "os" - - //"sigs.k8s.io/controller-runtime/pkg/webhook" - advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - - //"github.com/fluidos-project/node/controllers" - rearmanager "github.com/fluidos-project/node/pkg/rear-manager" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - - // to ensure that exec-entrypoint and run can make use of them. - _ "k8s.io/client-go/plugin/pkg/client/auth" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" -) - -var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") -) - -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(nodecorev1alpha1.AddToScheme(scheme)) - utilruntime.Must(advertisementv1alpha1.AddToScheme(scheme)) - utilruntime.Must(reservationv1alpha1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme -} - -func main() { - var metricsAddr string - var enableLeaderElection bool - var probeAddr string - flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") - flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") - flag.BoolVar(&enableLeaderElection, "leader-elect", false, - "Enable leader election for controller manager. "+ - "Enabling this will ensure there is only one active controller manager.") - opts := zap.Options{ - Development: true, - } - opts.BindFlags(flag.CommandLine) - flag.Parse() - - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) - - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, - HealthProbeBindAddress: probeAddr, - LeaderElection: enableLeaderElection, - LeaderElectionID: "586b6b69.fluidos.eu", - }) - if err != nil { - setupLog.Error(err, "unable to start manager") - os.Exit(1) - } - - if err = (&rearmanager.SolverReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Solver") - os.Exit(1) - } - - if err = (&rearmanager.AllocationReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Allocation") - os.Exit(1) - } - //+kubebuilder:scaffold:builder - - if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up health check") - os.Exit(1) - } - if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up ready check") - os.Exit(1) - } - - //av := rearmanager.NewValidator(mgr.GetClient()) - - //mgr.GetWebhookServer().Register("/validate/allocation", &webhook.Admission{Handler: av}) - - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) - } -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// LiqoCredentials contains the credentials of a Liqo cluster to enstablish a peering. -type LiqoCredentials struct { - ClusterID string `json:"clusterID"` - ClusterName string `json:"clusterName"` - Token string `json:"token"` - Endpoint string `json:"endpoint"` -} - -// ContractSpec defines the desired state of Contract -type ContractSpec struct { - - // TODO: ClusterID is the ID used by Liqo to search a contract during the peering phase. - - // This is the flavour on which the contract is based. It is used to lifetime maintain the critical characteristics of the contract. - Flavour nodecorev1alpha1.Flavour `json:"flavour"` - - // TransactionID is the ID of the transaction that this contract is part of - TransactionID string `json:"transactionID"` - - // The partition represents the dimension of the resources sold/bought. - // So it will reflect the dimension of the resources allocated on the remote cluster and reflected on the local virtual node. - Partition *Partition `json:"partition,omitempty"` - - // This is the Node identity of the buyer FLUIDOS Node. - Buyer nodecorev1alpha1.NodeIdentity `json:"buyer"` - - // BuyerClusterID is the Liqo ClusterID used by the seller to search a contract and the related resources during the peering phase. - BuyerClusterID string `json:"buyerClusterID"` - - // This is the Node identity of the seller FLUIDOS Node. - Seller nodecorev1alpha1.NodeIdentity `json:"seller"` - - // This credentials will be used by the customer to connect and enstablish a peering with the seller FLUIDOS Node through Liqo. - SellerCredentials LiqoCredentials `json:"sellerCredentials"` - - // This is the expiration time of the contract. It can be empty if the contract is not time limited. - ExpirationTime string `json:"expirationTime,omitempty"` - - // This contains additional information about the contract if needed. - ExtraInformation map[string]string `json:"extraInformation,omitempty"` -} - -// ContractStatus defines the observed state of Contract -type ContractStatus struct { - - // This is the status of the contract. - Phase nodecorev1alpha1.PhaseStatus `json:"phase"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// Contract is the Schema for the contracts API -type Contract struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ContractSpec `json:"spec,omitempty"` - Status ContractStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// ContractList contains a list of Contract -type ContractList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Contract `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Contract{}, &ContractList{}) -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package v1alpha1 contains API Schema definitions for the reservation v1alpha1 API group -// +kubebuilder:object:generate=true -// +groupName=reservation.fluidos.eu -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "reservation.fluidos.eu", Version: "v1alpha1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} - - // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme -) -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type Partition struct { - Architecture string `json:"architecture"` - Cpu resource.Quantity `json:"cpu"` - Memory resource.Quantity `json:"memory"` - Gpu resource.Quantity `json:"gpu,omitempty"` - EphemeralStorage resource.Quantity `json:"ephemeral-storage,omitempty"` - Storage resource.Quantity `json:"storage,omitempty"` -} - -// ReservationSpec defines the desired state of Reservation -type ReservationSpec struct { - - // SolverID is the ID of the solver that asks for the reservation - SolverID string `json:"solverID"` - - // This is the Node identity of the buyer FLUIDOS Node. - Buyer nodecorev1alpha1.NodeIdentity `json:"buyer"` - - // BuyerClusterID is the Liqo ClusterID used by the seller to search a contract and the related resources during the peering phase. - BuyerClusterID string `json:"buyerClusterID"` - - // This is the Node identity of the seller FLUIDOS Node. - Seller nodecorev1alpha1.NodeIdentity `json:"seller"` - - // Parition is the partition of the flavour that is being reserved - Partition *Partition `json:"partition,omitempty"` - - // Reserve indicates if the reservation is a reserve or not - Reserve bool `json:"reserve,omitempty"` - - // Purchase indicates if the reservation is an purchase or not - Purchase bool `json:"purchase,omitempty"` - - // PeeringCandidate is the reference to the PeeringCandidate of the Reservation - PeeringCandidate nodecorev1alpha1.GenericRef `json:"peeringCandidate,omitempty"` -} - -// ReservationStatus defines the observed state of Reservation -type ReservationStatus struct { - // This is the current phase of the reservation - Phase nodecorev1alpha1.PhaseStatus `json:"phase"` - - // ReservePhase is the current phase of the reservation - ReservePhase nodecorev1alpha1.Phase `json:"reservePhase,omitempty"` - - // PurchasePhase is the current phase of the reservation - PurchasePhase nodecorev1alpha1.Phase `json:"purchasePhase,omitempty"` - - // TransactionID is the ID of the transaction that this reservation is part of - TransactionID string `json:"transactionID,omitempty"` - - // Contract is the reference to the Contract of the Reservation - Contract nodecorev1alpha1.GenericRef `json:"contract,omitempty"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// +kubebuilder:printcolumn:name="Solver ID",type=string,JSONPath=`.spec.solverID` -// +kubebuilder:printcolumn:name="Reserve",type=boolean,JSONPath=`.spec.reserve` -// +kubebuilder:printcolumn:name="Purchase",type=boolean,JSONPath=`.spec.purchase` -// +kubebuilder:printcolumn:name="Seller",type=string,JSONPath=`.spec.seller.name` -// +kubebuilder:printcolumn:name="Peering Candidate",type=string,priority=1,JSONPath=`.spec.peeringCandidate.name` -// +kubebuilder:printcolumn:name="Transaction ID",type=string,JSONPath=`.status.transactionID` -// +kubebuilder:printcolumn:name="Reserve Phase",type=string,priority=1,JSONPath=`.status.reservePhase.phase` -// +kubebuilder:printcolumn:name="Purchase Phase",type=string,priority=1,JSONPath=`.status.purchasePhase.phase` -// +kubebuilder:printcolumn:name="Contract Name",type=string,JSONPath=`.status.contract.name` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase.phase` -// +kubebuilder:printcolumn:name="Message",type=string,priority=1,JSONPath=`.status.phase.message` -// Reservation is the Schema for the reservations API -type Reservation struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ReservationSpec `json:"spec,omitempty"` - Status ReservationStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// ReservationList contains a list of Reservation -type ReservationList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Reservation `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Reservation{}, &ReservationList{}) -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// TransactionSpec defines the desired state of Transaction -type TransactionSpec struct { - // FlavourID is the ID of the flavour that is being reserved - FlavourID string `json:"flavourID"` - - // Buyer is the buyer Identity of the Fluidos Node that is reserving the Flavour - Buyer nodecorev1alpha1.NodeIdentity `json:"buyer"` - - // ClusterID is the Liqo ClusterID of the Fluidos Node that is reserving the Flavour - ClusterID string `json:"clusterID"` - - // Partition is the partition of the flavour that is being reserved - Partition *Partition `json:"partition,omitempty"` - - // StartTime is the time at which the reservation should start - StartTime string `json:"startTime,omitempty"` -} - -// TransactionStatus defines the observed state of Transaction -type TransactionStatus struct { - // This is the current phase of the reservation - Phase nodecorev1alpha1.PhaseStatus `json:"phase"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// Transaction is the Schema for the transactions API -type Transaction struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec TransactionSpec `json:"spec,omitempty"` - Status TransactionStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// TransactionList contains a list of Transaction -type TransactionList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Transaction `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Transaction{}, &TransactionList{}) -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/tools" -) - -// SetPhase sets the phase of the discovery -func (r *Reservation) SetPhase(phase nodecorev1alpha1.Phase, msg string) { - r.Status.Phase.Phase = phase - r.Status.Phase.LastChangeTime = tools.GetTimeNow() - r.Status.Phase.Message = msg -} - -// SetReserveStatus sets the status of the reserve (if it is a reserve) -func (r *Reservation) SetReserveStatus(status nodecorev1alpha1.Phase) { - r.Status.ReservePhase = status -} - -// SetPurchaseStatus sets the status of the purchase (if it is a purchase) -func (r *Reservation) SetPurchaseStatus(status nodecorev1alpha1.Phase) { - r.Status.PurchasePhase = status -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// PeeringCandidateSpec defines the desired state of PeeringCandidate -type PeeringCandidateSpec struct { - Reserved bool `json:"reserved"` - - SolverID string `json:"solverID"` - - Flavour nodecorev1alpha1.Flavour `json:"flavour"` -} - -// PeeringCandidateStatus defines the observed state of PeeringCandidate -type PeeringCandidateStatus struct { - - // This field represents the creation time of the PeeringCandidate. - CreationTime string `json:"creationTime"` - - // This field represents the last update time of the PeeringCandidate. - LastUpdateTime string `json:"lastUpdateTime"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// PeeringCandidate is the Schema for the peeringcandidates API -type PeeringCandidate struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec PeeringCandidateSpec `json:"spec,omitempty"` - Status PeeringCandidateStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// PeeringCandidateList contains a list of PeeringCandidate -type PeeringCandidateList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []PeeringCandidate `json:"items"` -} - -func init() { - SchemeBuilder.Register(&PeeringCandidate{}, &PeeringCandidateList{}) -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package v1alpha1 contains API Schema definitions for the advertisement v1alpha1 API group -// +kubebuilder:object:generate=true -// +groupName=advertisement.fluidos.eu -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "advertisement.fluidos.eu", Version: "v1alpha1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} - - // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme -) -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// DiscoverySpec defines the desired state of Discovery -type DiscoverySpec struct { - - // This is the Solver ID of the solver that creates and so asks for the discovery. - // This is a reference to the Solver CRD - SolverID string `json:"solverID"` - - // This is the FlavourSelector that describes the characteristics of the intent that the solver is looking to satisfy - // This pattern corresponds to what has been defined in the REAR Protocol to do a discovery with a selector - Selector *nodecorev1alpha1.FlavourSelector `json:"selector"` - - // This flag indicates that needs to be enstablished a subscription to the provider in case a match is found. - // In order to have periodic updates of the status of the matching Flavour - Subscribe bool `json:"subscribe"` -} - -// DiscoveryStatus defines the observed state of Discovery -type DiscoveryStatus struct { - - // This is the current phase of the discovery - Phase nodecorev1alpha1.PhaseStatus `json:"phase"` - - // This is the reference to the PeeringCandidate CRD that is the result of the discovery if a match is found - PeeringCandidate nodecorev1alpha1.GenericRef `json:"peeringCandidate,omitempty"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// +kubebuilder:printcolumn:name="Solver ID",type=string,JSONPath=`.spec.solverID` -// +kubebuilder:printcolumn:name="Subscribe",type=boolean,JSONPath=`.spec.subscribe` -// +kubebuilder:printcolumn:name="PC Namespace",type=string,JSONPath=`.status.peeringCandidate.namespace` -// +kubebuilder:printcolumn:name="PC Name",type=string,JSONPath=`.status.peeringCandidate.name` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase.phase` -// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.phase.message` -// Discovery is the Schema for the discoveries API -type Discovery struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec DiscoverySpec `json:"spec,omitempty"` - Status DiscoveryStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// DiscoveryList contains a list of Discovery -type DiscoveryList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Discovery `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Discovery{}, &DiscoveryList{}) -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/tools" -) - -// SetPhase sets the phase of the discovery -func (d *Discovery) SetPhase(phase nodecorev1alpha1.Phase, msg string) { - d.Status.Phase.Phase = phase - d.Status.Phase.LastChangeTime = tools.GetTimeNow() - d.Status.Phase.Message = msg -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type NodeType string -type Status string - -const ( - Node NodeType = "Node" - VirtualNode NodeType = "VirtualNode" -) - -const ( - Active Status = "Active" - Reserved Status = "Reserved" - Released Status = "Released" - Inactive Status = "Inactive" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// AllocationSpec defines the desired state of Allocation -type AllocationSpec struct { - - // CustomerID - - // This is the ID of the intent for which the allocation was created. It is used by the Node Orchestrator to identify the correct allocation for a given intent - IntentID string `json:"intentID"` - - // This is the corresponding Node or VirtualNode name - LocalNode string `json:"localNode"` - - // This specifies the type of the node: Node (Physical node of the cluster) or VirtualNode (Remote node owned by a different cluster) - Type NodeType `json:"type"` - - // This flag indicates if the allocation is a forwarding allocation, if true it represents only a placeholder to undertand that the cluster is just a proxy to another cluster - Forwarding bool `json:"forwarding"` - - // This Flavour describes the characteristics of the allocation, it is based on the Flavour CRD from which it was created - Flavour Flavour `json:"flavour"` - - // This is the dimension of the allocation, it is based on the Flavour CRD from which it was created - Partition FlavourSelector `json:"partition,omitempty"` -} - -// AllocationStatus defines the observed state of Allocation -type AllocationStatus struct { - - // This allow to know the current status of the allocation - Status Status `json:"status"` - - // The creation time of the allocation object - CreationTime metav1.Time `json:"creationTime"` - - // The last time the allocation was updated - LastUpdateTime metav1.Time `json:"lastUpdateTime"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// Allocation is the Schema for the allocations API -type Allocation struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec AllocationSpec `json:"spec,omitempty"` - Status AllocationStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// AllocationList contains a list of Allocation -type AllocationList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Allocation `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Allocation{}, &AllocationList{}) -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package v1alpha1 contains API Schema definitions for the nodecore v1alpha1 API group -// +kubebuilder:object:generate=true -// +groupName=nodecore.fluidos.eu -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "nodecore.fluidos.eu", Version: "v1alpha1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} - - // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme -) -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - resource "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type Phase string - -// PhaseStatus represents the status of a phase of the solver. I.e. the status of the REAR phases. -type PhaseStatus struct { - Phase Phase `json:"phase"` - Message string `json:"message,omitempty"` - StartTime string `json:"startTime,omitempty"` - LastChangeTime string `json:"lastChangeTime,omitempty"` - EndTime string `json:"endTime,omitempty"` -} - -// Selector represents the criteria for selecting Flavours. -/* type Selector struct { - FlavourType string `json:"type,omitempty"` - Architecture string `json:"architecture,omitempty"` - Cpu int `json:"cpu,omitempty"` - Memory int `json:"memory,omitempty"` - EphemeralStorage int `json:"ephemeral-storage,omitempty"` - MoreThanCpu int `json:"moreThanCpu,omitempty"` - MoreThanMemory int `json:"moreThanMemory,omitempty"` - MoreThanEph int `json:"moreThanEph,omitempty"` - LessThanCpu int `json:"lessThanCpu,omitempty"` - LessThanMemory int `json:"lessThanMemory,omitempty"` - LessThanEph int `json:"lessThanEph,omitempty"` -} */ - -type FlavourSelector struct { - FlavourType string `json:"type"` - Architecture string `json:"architecture"` - RangeSelector *RangeSelector `json:"rangeSelector,omitempty"` - MatchSelector *MatchSelector `json:"matchSelector,omitempty"` -} - -// MatchSelector represents the criteria for selecting Flavours through a strict match. -type MatchSelector struct { - Cpu resource.Quantity `json:"cpu"` - Memory resource.Quantity `json:"memory"` - Storage resource.Quantity `json:"storage,omitempty"` - EphemeralStorage resource.Quantity `json:"ephemeralStorage,omitempty"` - Gpu resource.Quantity `json:"gpu,omitempty"` -} - -// RangeSelector represents the criteria for selecting Flavours through a range. -type RangeSelector struct { - MinCpu resource.Quantity `json:"minCpu,omitempty"` - MinMemory resource.Quantity `json:"minMemory,omitempty"` - MinEph resource.Quantity `json:"minEph,omitempty"` - MinStorage resource.Quantity `json:"minStorage,omitempty"` - MinGpu resource.Quantity `json:"minGpu,omitempty"` - MaxCpu resource.Quantity `json:"MaxCpu,omitempty"` - MaxMemory resource.Quantity `json:"MaxMemory,omitempty"` - MaxEph resource.Quantity `json:"MaxEph,omitempty"` - MaxStorage resource.Quantity `json:"MaxStorage,omitempty"` - MaxGpu resource.Quantity `json:"MaxGpu,omitempty"` -} - -// SolverSpec defines the desired state of Solver -type SolverSpec struct { - - // Selector contains the flavour requirements for the solver. - Selector *FlavourSelector `json:"selector,omitempty"` - - // IntentID is the ID of the intent that the Node Orchestrator is trying to solve. - // It is used to link the solver with the intent. - IntentID string `json:"intentID"` - - // FindCandidate is a flag that indicates if the solver should find a candidate to solve the intent. - FindCandidate bool `json:"findCandidate,omitempty"` - - // ReserveAndBuy is a flag that indicates if the solver should reserve and buy the resources on the candidate. - ReserveAndBuy bool `json:"reserveAndBuy,omitempty"` - - // EnstablishPeering is a flag that indicates if the solver should enstablish a peering with the candidate. - EnstablishPeering bool `json:"enstablishPeering,omitempty"` -} - -// SolverStatus defines the observed state of Solver -type SolverStatus struct { - - // FindCandidate describes the status of research of the candidate. - // Rear Manager is looking for the best candidate Flavour to solve the Node Orchestrator request. - FindCandidate Phase `json:"findCandidate,omitempty"` - - // ReserveAndBuy describes the status of the reservation and purchase of selected Flavour. - // Rear Manager is trying to reserve and purchase the resources on the candidate FLUIDOS Node. - ReserveAndBuy Phase `json:"reserveAndBuy,omitempty"` - - // Peering describes the status of the peering with the candidate. - // Rear Manager is trying to enstablish a peering with the candidate FLUIDOS Node. - Peering Phase `json:"peering,omitempty"` - - // DiscoveryPhase describes the status of the Discovery where the Discovery Manager - // is looking for matching flavours outside the FLUIDOS Node - DiscoveryPhase Phase `json:"discoveryPhase,omitempty"` - - // ReservationPhase describes the status of the Reservation where the Contract Manager - // is reserving and purchasing the resources on the candidate node. - ReservationPhase Phase `json:"reservationPhase,omitempty"` - - // ConsumePhase describes the status of the Consume phase where the VFM (Liqo) is enstablishing - // a peering with the candidate node. - ConsumePhase Phase `json:"consumePhase,omitempty"` - - // SolverPhase describes the status of the Solver generated by the Node Orchestrator. - // It is usefull to understand if the solver is still running or if it has finished or failed. - SolverPhase PhaseStatus `json:"solverPhase,omitempty"` - - // PeeringCandidate contains the candidate that the solver has eventually found to solve the intent. - PeeringCandidate GenericRef `json:"peeringCandidate,omitempty"` - - // Allocation contains the allocation that the solver has eventually created for the intent. - // It can correspond to a virtual node - // The Node Orchestrator will use this allocation to fullfill the intent. - Allocation GenericRef `json:"allocation,omitempty"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// Solver is the Schema for the solvers API -// +kubebuilder:printcolumn:name="Intent ID",type=string,JSONPath=`.spec.intentID` -// +kubebuilder:printcolumn:name="Find Candidate",type=boolean,JSONPath=`.spec.findCandidate` -// +kubebuilder:printcolumn:name="Reserve and Buy",type=boolean,JSONPath=`.spec.reserveAndBuy` -// +kubebuilder:printcolumn:name="Peering",type=boolean,JSONPath=`.spec.enstablishPeering` -// +kubebuilder:printcolumn:name="Candidate Phase",type=string,priority=1,JSONPath=`.status.findCandidate` -// +kubebuilder:printcolumn:name="Reserving Phase",type=string,priority=1,JSONPath=`.status.reserveAndBuy` -// +kubebuilder:printcolumn:name="Peering Phase",type=string,priority=1,JSONPath=`.status.peering` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.solverPhase.phase` -// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.solverPhase.message` -// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -// Solver is the Schema for the solvers API -type Solver struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec SolverSpec `json:"spec,omitempty"` - Status SolverStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// SolverList contains a list of Solver -type SolverList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Solver `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Solver{}, &SolverList{}) -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - K8S FlavourType = "k8s-fluidos" -) - -type FlavourType string - -type Characteristics struct { - - // Architecture is the architecture of the Flavour. - Architecture string `json:"architecture"` - - // CPU is the number of CPU cores of the Flavour. - Cpu resource.Quantity `json:"cpu"` - - // Memory is the amount of RAM of the Flavour. - Memory resource.Quantity `json:"memory"` - - // GPU is the number of GPU cores of the Flavour. - Gpu resource.Quantity `json:"gpu,omitempty"` - - // EphemeralStorage is the amount of ephemeral storage of the Flavour. - EphemeralStorage resource.Quantity `json:"ephemeral-storage,omitempty"` - - // PersistentStorage is the amount of persistent storage of the Flavour. - PersistentStorage resource.Quantity `json:"persistent-storage,omitempty"` -} - -type Policy struct { - - // Partitionable contains the partitioning properties of the Flavour. - Partitionable *Partitionable `json:"partitionable,omitempty"` - - // Aggregatable contains the aggregation properties of the Flavour. - Aggregatable *Aggregatable `json:"aggregatable,omitempty"` -} - -// Partitionable represents the partitioning properties of a Flavour, such as the minimum and incremental values of CPU and RAM. -type Partitionable struct { - // CpuMin is the minimum requirable number of CPU cores of the Flavour. - CpuMin resource.Quantity `json:"cpuMin"` - - // MemoryMin is the minimum requirable amount of RAM of the Flavour. - MemoryMin resource.Quantity `json:"memoryMin"` - - // CpuStep is the incremental value of CPU cores of the Flavour. - CpuStep resource.Quantity `json:"cpuStep"` - - // MemoryStep is the incremental value of RAM of the Flavour. - MemoryStep resource.Quantity `json:"memoryStep"` -} - -// Aggregatable represents the aggregation properties of a Flavour, such as the minimum instance count. -type Aggregatable struct { - // MinCount is the minimum requirable number of instances of the Flavour. - MinCount int `json:"minCount"` - - // MaxCount is the maximum requirable number of instances of the Flavour. - MaxCount int `json:"maxCount"` -} - -type Price struct { - - // Amount is the amount of the price. - Amount string `json:"amount"` - - // Currency is the currency of the price. - Currency string `json:"currency"` - - // Period is the period of the price. - Period string `json:"period"` -} - -type OptionalFields struct { - - // Availability is the availability flag of the Flavour. - // It is a field inherited from the REAR Protocol specifications. - Availability bool `json:"availability,omitempty"` - - // WorkerID is the ID of the worker that provides the Flavour. - WorkerID string `json:"workerID,omitempty"` -} - -// FlavourSpec defines the desired state of Flavour -type FlavourSpec struct { - // This specs are based on the REAR Protocol specifications. - - // FlavourID is the name of the Flavour. - FlavourID string `json:"flavourID"` - - // ProviderID is the ID of the FLUIDOS Node ID that provides this Flavour. - // It can correspond to ID of the owner FLUIDOS Node or to the ID of a FLUIDOS SuperNode that represents the entry point to a FLUIDOS Domain - ProviderID string `json:"providerID"` - - // Type is the type of the Flavour. Currently, only K8S is supported. - Type FlavourType `json:"type"` - - // Characteristics contains the characteristics of the Flavour. - // They are based on the type of the Flavour and can change depending on it. In this case, the type is K8S so the characteristics are CPU, Memory, GPU and EphemeralStorage. - Characteristics Characteristics `json:"characteristics"` - - // Policy contains the policy of the Flavour. The policy describes the partitioning and aggregation properties of the Flavour. - Policy Policy `json:"policy"` - - // Owner contains the identity info of the owner of the Flavour. It can be unknown if the Flavour is provided by a reseller or a third party. - Owner NodeIdentity `json:"owner"` - - // Price contains the price model of the Flavour. - Price Price `json:"price"` - - // This field is used to specify the optional fields that can be retrieved from the Flavour. - // In the future it will be expanded to include more optional fields defined in the REAR Protocol or custom ones. - OptionalFields OptionalFields `json:"optionalFields"` -} - -// FlavourStatus defines the observed state of Flavour -type FlavourStatus struct { - - // This field represents the expiration time of the Flavour. It is used to determine when the Flavour is no longer valid. - ExpirationTime string `json:"expirationTime"` - - // This field represents the creation time of the Flavour. - CreationTime string `json:"creationTime"` - - // This field represents the last update time of the Flavour. - LastUpdateTime string `json:"lastUpdateTime"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// Flavour is the Schema for the flavours API -type Flavour struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec FlavourSpec `json:"spec,omitempty"` - Status FlavourStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// FlavourList contains a list of Flavour -type FlavourList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Flavour `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Flavour{}, &FlavourList{}) -} -skip file ./apis/nodecore/v1alpha1/common.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - "github.com/fluidos-project/node/pkg/utils/tools" -) - -func (solver *Solver) SetPhase(phase Phase, msg string) { - t := tools.GetTimeNow() - solver.Status.SolverPhase.Phase = phase - solver.Status.SolverPhase.LastChangeTime = t - solver.Status.SolverPhase.Message = msg - solver.Status.SolverPhase.EndTime = t -} - -// SetPurchasePhase sets the ReserveAndBuy phase of the solver -func (solver *Solver) SetReserveAndBuyStatus(phase Phase) { - solver.Status.ReserveAndBuy = phase - solver.Status.SolverPhase.LastChangeTime = tools.GetTimeNow() -} - -// SetFindCandidateStatus sets the FindCandidate phase of the solver -func (solver *Solver) SetFindCandidateStatus(phase Phase) { - solver.Status.FindCandidate = phase - solver.Status.SolverPhase.LastChangeTime = tools.GetTimeNow() -} - -// SetDiscoveryStatus sets the discovery phase of the solver -func (solver *Solver) SetDiscoveryStatus(phase Phase) { - solver.Status.DiscoveryPhase = phase - solver.Status.SolverPhase.LastChangeTime = tools.GetTimeNow() -} - -// SetReservationStatus sets the reservation phase of the solver -func (solver *Solver) SetReservationStatus(phase Phase) { - solver.Status.ReservationPhase = phase - solver.Status.SolverPhase.LastChangeTime = tools.GetTimeNow() -} -/* -Copyright 2023. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package contractmanager - -import ( - "context" - - advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - "github.com/fluidos-project/node/pkg/rear-controller/gateway" - "github.com/fluidos-project/node/pkg/utils/namings" - "github.com/fluidos-project/node/pkg/utils/resourceforge" - "github.com/fluidos-project/node/pkg/utils/tools" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// ReservationReconciler reconciles a Reservation object -type ReservationReconciler struct { - client.Client - Scheme *runtime.Scheme - Gateway *gateway.Gateway -} - -// clusterRole -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=reservations,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=reservations/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=reservations/finalizers,verbs=update -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=contracts,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=contracts/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=contracts/finalizers,verbs=update -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=transactions,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=transactions/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=transactions/finalizers,verbs=update -//+kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.11.2/pkg/reconcile -func (r *ReservationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - klog.Info("Reconciling Reservation") - log := ctrl.LoggerFrom(ctx, "reservation", req.NamespacedName) - ctx = ctrl.LoggerInto(ctx, log) - - // var contract *reservationv1alpha1.Contract - - var reservation reservationv1alpha1.Reservation - if err := r.Get(ctx, req.NamespacedName, &reservation); client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when getting Reservation %s before reconcile: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } else if err != nil { - klog.Infof("Reservation %s not found, probably deleted", req.NamespacedName) - return ctrl.Result{}, nil - } - - var peeringCandidate advertisementv1alpha1.PeeringCandidate - if err := r.Get(ctx, client.ObjectKey{ - Name: reservation.Spec.PeeringCandidate.Name, - Namespace: reservation.Spec.PeeringCandidate.Namespace, - }, &peeringCandidate); err != nil { - klog.Errorf("Error when getting PeeringCandidate %s before reconcile: %s", req.NamespacedName, err) - reservation.SetPhase(nodecorev1alpha1.PhaseFailed, "Reservation failed: error when getting PeeringCandidate") - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status before reconcile: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, err - } - - if reservation.Status.Phase.Phase != nodecorev1alpha1.PhaseSolved && - reservation.Status.Phase.Phase != nodecorev1alpha1.PhaseTimeout && - reservation.Status.Phase.Phase != nodecorev1alpha1.PhaseFailed && - reservation.Status.Phase.Phase != nodecorev1alpha1.PhaseRunning && - reservation.Status.Phase.Phase != nodecorev1alpha1.PhaseIdle { - - klog.Infof("Reservation %s started", reservation.Name) - reservation.Status.Phase.StartTime = tools.GetTimeNow() - reservation.SetPhase(nodecorev1alpha1.PhaseRunning, "Reservation started") - reservation.SetReserveStatus(nodecorev1alpha1.PhaseIdle) - reservation.SetPurchaseStatus(nodecorev1alpha1.PhaseIdle) - - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status before reconcile: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - if reservation.Spec.Reserve { - reservePhase := reservation.Status.ReservePhase - switch reservePhase { - case nodecorev1alpha1.PhaseRunning: - klog.Infof("Reservation %s: Reserve phase running", reservation.Name) - flavourID := namings.RetrieveFlavourNameFromPC(reservation.Spec.PeeringCandidate.Name) - res, err := r.Gateway.ReserveFlavour(ctx, &reservation, flavourID) - if err != nil { - if res != nil { - klog.Infof("Transaction is non correctly set, Retrying...") - return ctrl.Result{Requeue: true}, nil - } - klog.Errorf("Error when reserving flavour for Reservation %s: %s", req.NamespacedName, err) - reservation.SetReserveStatus(nodecorev1alpha1.PhaseFailed) - reservation.SetPhase(nodecorev1alpha1.PhaseFailed, "Reservation failed: error when reserving flavour") - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, err - } - - klog.Infof("Transaction: %v", res) - - // Create a Transaction CR starting from the transaction object - transaction := resourceforge.ForgeTransactionFromObj(res) - - if err := r.Create(ctx, transaction); err != nil { - klog.Errorf("Error when creating Transaction %s: %s", transaction.Name, err) - return ctrl.Result{}, err - } - - klog.Infof("Transaction %s created", transaction.Name) - reservation.Status.TransactionID = res.TransactionID - reservation.SetReserveStatus(nodecorev1alpha1.PhaseSolved) - - // Update the status for reconcile - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - - return ctrl.Result{}, nil - - case nodecorev1alpha1.PhaseSolved: - klog.Infof("Reserve %s solved", reservation.Name) - case nodecorev1alpha1.PhaseFailed: - klog.Infof("Reserve %s failed", reservation.Name) - return ctrl.Result{}, nil - case nodecorev1alpha1.PhaseIdle: - klog.Infof("Reserve %s idle", reservation.Name) - reservation.SetReserveStatus(nodecorev1alpha1.PhaseRunning) - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - default: - klog.Infof("Reserve %s unknown phase", reservation.Name) - reservation.SetReserveStatus(nodecorev1alpha1.PhaseIdle) - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - } - - if reservation.Spec.Purchase && reservation.Status.ReservePhase == nodecorev1alpha1.PhaseSolved { - purchasePhase := reservation.Status.PurchasePhase - switch purchasePhase { - case nodecorev1alpha1.PhaseIdle: - klog.Infof("Purchase phase for the reservation %s idle, starting...", reservation.Name) - reservation.SetPurchaseStatus(nodecorev1alpha1.PhaseRunning) - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - case nodecorev1alpha1.PhaseRunning: - if reservation.Status.TransactionID == "" { - klog.Infof("TransactionID not set for Reservation %s", reservation.Name) - reservation.SetPurchaseStatus(nodecorev1alpha1.PhaseFailed) - reservation.SetPhase(nodecorev1alpha1.PhaseFailed, "Reservation failed: TransactionID not set") - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - transactionID := reservation.Status.TransactionID - resPurchase, err := r.Gateway.PurchaseFlavour(ctx, transactionID, reservation.Spec.Seller) - if err != nil { - klog.Errorf("Error when purchasing flavour for Reservation %s: %s", req.NamespacedName, err) - reservation.SetPurchaseStatus(nodecorev1alpha1.PhaseFailed) - reservation.SetPhase(nodecorev1alpha1.PhaseFailed, "Reservation failed: error when purchasing flavour") - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, err - } - - klog.Infof("Purchase completed with status %s", resPurchase.Status) - - reservation.SetPurchaseStatus(nodecorev1alpha1.PhaseRunning) - - if err := r.Update(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s: %s", reservation.Name, err) - return ctrl.Result{}, err - } - - klog.Infof("Reservation %s updated", reservation.Name) - - // Create a contract CR now that the reservation is solved - contract := resourceforge.ForgeContractFromObj(resPurchase.Contract) - err = r.Create(ctx, contract) - if errors.IsAlreadyExists(err) { - klog.Errorf("Error when creating Contract %s: %s", contract.Name, err) - } else if err != nil { - klog.Errorf("Error when creating Contract %s: %s", contract.Name, err) - return ctrl.Result{}, err - } - klog.Infof("Contract %s created", contract.Name) - - reservation.SetPurchaseStatus(nodecorev1alpha1.PhaseSolved) - reservation.Status.Contract = nodecorev1alpha1.GenericRef{ - Name: contract.Name, - Namespace: contract.Namespace, - } - reservation.SetPhase(nodecorev1alpha1.PhaseSolved, "Reservation solved") - - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - - return ctrl.Result{}, nil - - case nodecorev1alpha1.PhaseFailed: - klog.Infof("Purchase phase for the reservation %s failed", reservation.Name) - return ctrl.Result{}, nil - - case nodecorev1alpha1.PhaseSolved: - klog.Infof("Purchase phase for the reservation %s solved", reservation.Name) - default: - klog.Infof("Purchase phase for the reservation %s unknown", reservation.Name) - reservation.SetPurchaseStatus(nodecorev1alpha1.PhaseIdle) - if err := r.updateReservationStatus(ctx, &reservation); err != nil { - klog.Errorf("Error when updating Reservation %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - } - - return ctrl.Result{}, nil -} - -// updateSolverStatus updates the status of the discovery -func (r *ReservationReconciler) updateReservationStatus(ctx context.Context, reservation *reservationv1alpha1.Reservation) error { - return r.Status().Update(ctx, reservation) -} - -// SetupWithManager sets up the controller with the Manager. -func (r *ReservationReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&reservationv1alpha1.Reservation{}). - Complete(r) -} -skip file ./pkg/rear-controller/contract-manager/models.go since no import -skip file ./pkg/rear-controller/contract-manager/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package discoverymanager - -import ( - "context" - "fmt" - "net/http" - - advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" - admissionv1 "k8s.io/api/admission/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -//+kubebuilder:webhook:path=/validate/peeringcandidate,mutating=false,failurePolicy=ignore,groups=advertisement.node.fluidos.io,resources=peeringcandidates,verbs=create;update;delete,versions=v1alpha1,name=pc.validate.fluidos.eu,sideEffects=None,admissionReviewVersions={v1,v1beta1} - -type PCValidator struct { - client client.Client - decoder *admission.Decoder -} - -func NewPCValidator(client client.Client) *PCValidator { - return &PCValidator{client: client, decoder: admission.NewDecoder(runtime.NewScheme())} -} - -func (v *PCValidator) Handle(ctx context.Context, req admission.Request) admission.Response { - - switch req.Operation { - case admissionv1.Create: - return v.HandleCreate(ctx, req) - case admissionv1.Delete: - return v.HandleDelete(ctx, req) - case admissionv1.Update: - return v.HandleUpdate(ctx, req) - default: - return admission.Errored(http.StatusBadRequest, fmt.Errorf("unsupported operation %q", req.Operation)) - } -} - -func (v *PCValidator) HandleCreate(ctx context.Context, req admission.Request) admission.Response { - pc, err := v.DecodePeeringCandidate(req.Object) - if err != nil { - klog.Errorf("Failed to decode peering candidate: %v", err) - return admission.Errored(http.StatusBadRequest, fmt.Errorf("failed to decode peering candidate: %v", err)) - } - - if pc.Spec.Reserved && pc.Spec.SolverID == "" { - return admission.Denied("If peering candidate is reserved, solver ID must be set") - } - - if !pc.Spec.Reserved && pc.Spec.SolverID != "" { - return admission.Denied("If peering candidate is not reserved, solver ID must not be set") - } - - return admission.Allowed("") -} - -func (v *PCValidator) HandleDelete(ctx context.Context, req admission.Request) admission.Response { - // Here we could check if the peering candidate is reserved and if so, we need to check if the solver ID matches the one of the solver that is deleting the peering candidate - // or if the solver ID is empty, we need to check if there is a Contract that is using this peering candidate - // Maybe this is not the right logic but it need to be discussed and implemented - return admission.Allowed("") -} - -func (v *PCValidator) HandleUpdate(ctx context.Context, req admission.Request) admission.Response { - pc, err := v.DecodePeeringCandidate(req.Object) - if err != nil { - klog.Errorf("Failed to decode peering candidate: %v", err) - return admission.Errored(http.StatusBadRequest, fmt.Errorf("failed to decode peering candidate: %v", err)) - } - - pcOld, err := v.DecodePeeringCandidate(req.OldObject) - if err != nil { - klog.Errorf("Failed to decode peering old candidate: %v", err) - return admission.Errored(http.StatusBadRequest, fmt.Errorf("failed to decode peering old candidate: %v", err)) - } - - // PC can be updated only if: - // - both Reserved flag and SolverID are not set (i.e. it is not reserved) - // - both Reserved flag and SolverID are set and you want to clear both in the same time - - if !pcOld.Spec.Reserved && pcOld.Spec.SolverID == "" { - return admission.Allowed("") - } - - if pcOld.Spec.Reserved && pcOld.Spec.SolverID != "" && !pc.Spec.Reserved && pc.Spec.SolverID == "" { - return admission.Allowed("") - } - - return admission.Denied("Peering candidate can be updated only if it is not reserved or if both Reserved flag and SolverID are set and you want to clear both in the same time") -} - -/* func (v *PCValidator) InjectDecoder(d *admission.Decoder) error { - v.decoder = d - return nil -} */ - -func (v *PCValidator) DecodePeeringCandidate(obj runtime.RawExtension) (pc *advertisementv1alpha1.PeeringCandidate, err error) { - pc = &advertisementv1alpha1.PeeringCandidate{} - err = v.decoder.DecodeRaw(obj, pc) - return -} -skip file ./pkg/rear-controller/discovery-manager/doc.go since no import -/* -Copyright 2023. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package discoverymanager - -import ( - "context" - - advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - gateway "github.com/fluidos-project/node/pkg/rear-controller/gateway" - resourceforge "github.com/fluidos-project/node/pkg/utils/resourceforge" - "github.com/fluidos-project/node/pkg/utils/tools" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// DiscoveryReconciler reconciles a Discovery object -type DiscoveryReconciler struct { - client.Client - Scheme *runtime.Scheme - Gateway *gateway.Gateway -} - -// clusterRole -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=peeringcandidates,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=peeringcandidates/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=peeringcandidates/finalizers,verbs=update -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=discoveries,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=discoveries/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=discoveries/finalizers,verbs=update -//+kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the Discovery object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.11.2/pkg/reconcile -func (r *DiscoveryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := ctrl.LoggerFrom(ctx, "discovery", req.NamespacedName) - ctx = ctrl.LoggerInto(ctx, log) - - var peeringCandidate *advertisementv1alpha1.PeeringCandidate - var peeringCandidateReserved advertisementv1alpha1.PeeringCandidate - - var discovery advertisementv1alpha1.Discovery - if err := r.Get(ctx, req.NamespacedName, &discovery); client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when getting Discovery %s before reconcile: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } else if err != nil { - klog.Infof("Discovery %s not found, probably deleted", req.NamespacedName) - return ctrl.Result{}, nil - } - - klog.Infof("Discovery %s started", discovery.Name) - - if discovery.Status.Phase.Phase != nodecorev1alpha1.PhaseSolved && - discovery.Status.Phase.Phase != nodecorev1alpha1.PhaseTimeout && - discovery.Status.Phase.Phase != nodecorev1alpha1.PhaseFailed && - discovery.Status.Phase.Phase != nodecorev1alpha1.PhaseRunning && - discovery.Status.Phase.Phase != nodecorev1alpha1.PhaseIdle { - - discovery.Status.Phase.StartTime = tools.GetTimeNow() - discovery.SetPhase(nodecorev1alpha1.PhaseRunning, "Discovery started") - - if err := r.updateDiscoveryStatus(ctx, &discovery); err != nil { - klog.Errorf("Error when updating Discovery %s status before reconcile: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - switch discovery.Status.Phase.Phase { - case nodecorev1alpha1.PhaseRunning: - flavours, err := r.Gateway.DiscoverFlavours(discovery.Spec.Selector) - if err != nil { - klog.Errorf("Error when getting Flavour: %s", err) - discovery.SetPhase(nodecorev1alpha1.PhaseFailed, "Error when getting Flavour") - if err := r.updateDiscoveryStatus(ctx, &discovery); err != nil { - klog.Errorf("Error when updating Discovery %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - if len(flavours) == 0 { - klog.Infof("No Flavours found") - discovery.SetPhase(nodecorev1alpha1.PhaseFailed, "No Flavours found") - if err := r.updateDiscoveryStatus(ctx, &discovery); err != nil { - klog.Errorf("Error when updating Discovery %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - klog.Infof("Flavours found: %d", len(flavours)) - - // TODO: check if a corresponding PeeringCandidate already exists!! - var first bool = true - for _, flavour := range flavours { - if first { - // We refer to the first peering candidate as the one that is reserved - peeringCandidate = resourceforge.ForgePeeringCandidate(flavour, discovery.Spec.SolverID, true) - peeringCandidateReserved = *peeringCandidate - first = false - } else { - // the others are created as not reserved - peeringCandidate = resourceforge.ForgePeeringCandidate(flavour, discovery.Spec.SolverID, false) - } - - err = r.Create(context.Background(), peeringCandidate) - if err != nil { - klog.Infof("Discovery %s failed: error while creating Peering Candidate", discovery.Name) - return ctrl.Result{}, err - } - } - - // Update the Discovery with the PeeringCandidate - discovery.Status.PeeringCandidate = nodecorev1alpha1.GenericRef{ - Name: peeringCandidateReserved.Name, - Namespace: peeringCandidateReserved.Namespace, - } - - discovery.SetPhase(nodecorev1alpha1.PhaseSolved, "Discovery Solved: Peering Candidate found") - if err := r.updateDiscoveryStatus(ctx, &discovery); err != nil { - klog.Errorf("Error when updating Discovery %s: %s", discovery.Name, err) - return ctrl.Result{}, err - } - klog.Infof("Discovery %s updated", discovery.Name) - - return ctrl.Result{}, nil - - case nodecorev1alpha1.PhaseSolved: - klog.Infof("Discovery %s solved", discovery.Name) - case nodecorev1alpha1.PhaseFailed: - klog.Infof("Discovery %s failed", discovery.Name) - } - - return ctrl.Result{}, nil -} - -// updateDiscoveryStatus updates the status of the discovery -func (r *DiscoveryReconciler) updateDiscoveryStatus(ctx context.Context, discovery *advertisementv1alpha1.Discovery) error { - return r.Status().Update(ctx, discovery) -} - -// SetupWithManager sets up the controller with the Manager. -func (r *DiscoveryReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&advertisementv1alpha1.Discovery{}). - Complete(r) -} -skip file ./pkg/rear-controller/discovery-manager/const.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpc - -import ( - context "context" - "fmt" - "log" - "net" - - "github.com/fluidos-project/node/pkg/utils/flags" - resourcemonitors "github.com/liqotech/liqo/pkg/liqo-controller-manager/resource-request-controller/resource-monitors" - grpc "google.golang.org/grpc" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type grpcServer struct { - Server *grpc.Server - client client.Client - //contractHandler connector.ContractHandler - stream resourcemonitors.ResourceReader_SubscribeServer - resourcemonitors.ResourceReaderServer -} - -func NewGrpcServer(cl client.Client) *grpcServer { - return &grpcServer{ - Server: grpc.NewServer(), - client: cl, - } -} - -func (s *grpcServer) Start(ctx context.Context) error { - grpcUrl := ":" + flags.GRPC_PORT - - // gRPC Configuration - klog.Info("Configuring gRPC Server") - lis, err := net.Listen("tcp", grpcUrl) - if err != nil { - klog.Infof("gRPC failed to listen: %v", err) - return fmt.Errorf("gRPC failed to listen: %v", err) - } - - klog.Infof("gRPC Server Listening on %s", grpcUrl) - // gRPC Server start listener - return s.Server.Serve(lis) -} - -/* func (s *grpcServer) RegisterContractHandler(ch connector.ContractHandler) { - s.contractHandler = ch -} */ - -func (s *grpcServer) ReadResources(ctx context.Context, req *resourcemonitors.ClusterIdentity) (*resourcemonitors.ResourceList, error) { - readResponse := &resourcemonitors.ResourceList{Resources: map[string]*resource.Quantity{}} - - log.Printf("ReadResource for clusterID %s", req.ClusterID) - resources, err := s.GetOfferResourcesByClusterID(req.ClusterID) - if err != nil { - // TODO: maybe should be resurned an empty resource list - return nil, err - } - - log.Printf("Retrieved resources for clusterID %s: %v", req.ClusterID, resources) - for key, value := range *resources { - readResponse.Resources[key.String()] = &value - } - - return readResponse, nil -} - -func (s *grpcServer) Subscribe(req *resourcemonitors.Empty, srv resourcemonitors.ResourceReader_SubscribeServer) error { - // Implement here your logic - s.stream = srv - ctx := srv.Context() - - fmt.Println("Subscribe") - - _ = s.NotifyChange(context.Background(), &resourcemonitors.ClusterIdentity{ClusterID: resourcemonitors.AllClusterIDs}) - - for { - select { - case <-ctx.Done(): - klog.Info("liqo controller manager disconnected") - return nil - } - } - -} - -func (s *grpcServer) NotifyChange(ctx context.Context, req *resourcemonitors.ClusterIdentity) error { - // Implement here your logic - if s.stream == nil { - return fmt.Errorf("you must first subscribe a controller manager to notify a change") - } else { - _ = s.stream.Send(req) - } - return nil -} - -func (s *grpcServer) RemoveCluster(ctx context.Context, req *resourcemonitors.ClusterIdentity) (*resourcemonitors.Empty, error) { - // Implement here your logic - return nil, fmt.Errorf("Not implemented") -} - -func (s *grpcServer) GetOfferResourcesByClusterID(clusterID string) (*corev1.ResourceList, error) { - log.Printf("Getting resources for cluster ID: %s", clusterID) - resources, err := getContractResourcesByClusterID(s.client, clusterID) - if err != nil { - return nil, err - } - return resources, nil -} - -func (s *grpcServer) UpdatePeeringOffer(clusterID string) { - _ = s.NotifyChange(context.Background(), &resourcemonitors.ClusterIdentity{ClusterID: clusterID}) -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpc - -import ( - "context" - "fmt" - - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - corev1 "k8s.io/api/core/v1" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func getContractResourcesByClusterID(cl client.Client, clusterID string) (*corev1.ResourceList, error) { - var contracts reservationv1alpha1.ContractList - - if err := cl.List(context.Background(), &contracts, client.MatchingFields{"spec.clusterID": clusterID}); err != nil { - if client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when listing Contracts: %s", err) - return nil, err - } - } - - if len(contracts.Items) == 0 { - klog.Errorf("No contracts found for cluster %s", clusterID) - return nil, fmt.Errorf("No contracts found for cluster %s", clusterID) - } - - if len(contracts.Items) > 1 { - resources := multipleContractLogic(contracts.Items) - return resources, nil - } - - contract := contracts.Items[0] - - return mapQuantityToResourceList(contract.Spec.Partition), nil -} - -func multipleContractLogic(contracts []reservationv1alpha1.Contract) *corev1.ResourceList { - resources := &corev1.ResourceList{} - for _, contract := range contracts { - resources = addResources(*resources, contract.Spec.Partition) - } - return resources -} - -// This function adds the resources of a contract to the existing resourceList -func addResources(resources corev1.ResourceList, partition *reservationv1alpha1.Partition) *corev1.ResourceList { - for key, value := range *mapQuantityToResourceList(partition) { - if prevRes, ok := resources[key]; !ok { - resources[key] = value - } else { - prevRes.Add(value) - resources[key] = prevRes - } - } - return &resources -} - -func mapQuantityToResourceList(partition *reservationv1alpha1.Partition) *corev1.ResourceList { - return &corev1.ResourceList{ - corev1.ResourceCPU: partition.Cpu, - corev1.ResourceMemory: partition.Memory, - corev1.ResourceEphemeralStorage: partition.EphemeralStorage, - corev1.ResourceStorage: partition.Storage, - } -} -skip file ./pkg/rear-controller/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "context" - "encoding/json" - "io" - "net/http" - - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/common" - "github.com/fluidos-project/node/pkg/utils/flags" - "github.com/fluidos-project/node/pkg/utils/models" - "github.com/fluidos-project/node/pkg/utils/namings" - "github.com/fluidos-project/node/pkg/utils/parseutil" - "github.com/fluidos-project/node/pkg/utils/resourceforge" - "github.com/fluidos-project/node/pkg/utils/services" - "github.com/fluidos-project/node/pkg/utils/tools" - "github.com/gorilla/mux" - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// TODO: all these functions should be moved into the REAR Gateway package - -// getFlavours gets all the flavours CRs from the cluster -func (g *Gateway) getFlavours(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - klog.Infof("Processing request for getting all Flavours...") - - flavours, err := services.GetAllFlavours(g.client) - if err != nil { - klog.Errorf("Error getting all the Flavour CRs: %s", err) - http.Error(w, "Error getting all the Flavour CRs", http.StatusInternalServerError) - return - } - - klog.Infof("Found %d Flavours in the cluster", len(flavours)) - - // Filtering only the available flavours - for i, f := range flavours { - if !f.Spec.OptionalFields.Availability { - flavours = append(flavours[:i], flavours[i+1:]...) - } - } - - klog.Infof("Available Flavours: %d", len(flavours)) - if len(flavours) == 0 { - klog.Infof("No available Flavours found") - http.Error(w, "No Flavours found", http.StatusNotFound) - return - } - - // Select the flavour with the max CPU - max := resource.MustParse("0") - var selected nodecorev1alpha1.Flavour - for _, f := range flavours { - if f.Spec.Characteristics.Cpu.Cmp(max) == 1 { - max = f.Spec.Characteristics.Cpu - selected = f - } - } - - klog.Infof("Flavour %s selected - Parsing...", selected.Name) - parsed := parseutil.ParseFlavour(selected) - - klog.Infof("Flavour parsed: %v", parsed) - - // Encode the Flavour as JSON and write it to the response writer - encodeResponse(w, parsed) -} - -// getFlavourByID gets the flavour CR from the cluster that matches the flavourID -/* func (g *Gateway) getFlavourByID(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - // Get the flavourID from the URL - params := mux.Vars(r) - flavourID := params["flavourID"] - - // Get the Flavour that matches the flavourID - flavour, err := services.GetFlavourByID(flavourID, g.client) - if err != nil { - http.Error(w, "Error getting the Flavour by ID", http.StatusInternalServerError) - return - } - - if flavour == nil { - http.Error(w, "No Flavour found", http.StatusNotFound) - return - } - - flavourParsed := parseutil.ParseFlavour(*flavour) - - klog.Infof("Flavour found is: %s", flavourParsed.FlavourID) - - // Encode the FlavourList as JSON and write it to the response writer - encodeResponse(w, flavourParsed) - -} */ - -// getFlavourBySelectorHandler gets the flavour CRs from the cluster that match the selector -func (g *Gateway) getFlavoursBySelector(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - klog.Infof("Processing request for getting Flavours by selector...") - - // Read the request body - body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // build the selector from the request body - selector, err := buildSelector(body) - if err != nil { - klog.Errorf("Error building the selector: %s", err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - flavours, err := services.GetAllFlavours(g.client) - if err != nil { - klog.Errorf("Error getting all the Flavour CRs: %s", err) - http.Error(w, "Error getting all the Flavour CRs", http.StatusInternalServerError) - return - } - - klog.Infof("Found %d Flavours in the cluster", len(flavours)) - - // Filtering only the available flavours - for i, f := range flavours { - if !f.Spec.OptionalFields.Availability { - flavours = append(flavours[:i], flavours[i+1:]...) - } - } - - klog.Infof("Available Flavours: %d", len(flavours)) - if len(flavours) == 0 { - klog.Infof("No available Flavours found") - http.Error(w, "No Flavours found", http.StatusNotFound) - return - } - - klog.Infof("Checking selector syntax...") - if err := common.CheckSelector(selector); err != nil { - klog.Errorf("Error checking the selector syntax: %s", err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - klog.Infof("Filtering Flavours by selector...") - flavoursSelected, err := common.FilterFlavoursBySelector(flavours, selector) - if err != nil { - http.Error(w, "Error getting the Flavours by selector", http.StatusInternalServerError) - return - } - - klog.Infof("Flavours found that match the selector are: %d", len(flavoursSelected)) - - if len(flavoursSelected) == 0 { - klog.Infof("No matching Flavours found") - http.Error(w, "No Flavours found", http.StatusNotFound) - return - } - - // Select the flavour with the max CPU - max := resource.MustParse("0") - var selected nodecorev1alpha1.Flavour - for _, f := range flavoursSelected { - if f.Spec.Characteristics.Cpu.Cmp(max) == 1 { - max = f.Spec.Characteristics.Cpu - selected = f - } - } - - klog.Infof("Flavour %s selected - Parsing...", selected.Name) - parsed := parseutil.ParseFlavour(selected) - - klog.Infof("Flavour parsed: %v", parsed) - - // Encode the Flavour as JSON and write it to the response writer - encodeResponse(w, parsed) -} - -// reserveFlavour reserves a Flavour by its flavourID -func (g *Gateway) reserveFlavour(w http.ResponseWriter, r *http.Request) { - // Get the flavourID value from the URL parameters - params := mux.Vars(r) - flavourID := params["flavourID"] - var transaction models.Transaction - var request models.ReserveRequest - - if err := json.NewDecoder(r.Body).Decode(&request); err != nil { - klog.Errorf("Error decoding the ReserveRequest: %s", err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - if flavourID != request.FlavourID { - klog.Infof("Mismatch body & param: %s != %s", flavourID, request.FlavourID) - http.Error(w, "Mismatch body & param", http.StatusConflict) - return - } - - // Check if the Transaction already exists - t, found := g.SearchTransaction(request.Buyer.NodeID, flavourID) - if found { - t.StartTime = tools.GetTimeNow() - transaction = t - g.addNewTransacion(t) - } - - if !found { - klog.Infof("Reserving flavour %s started", flavourID) - - flavour, _ := services.GetFlavourByID(flavourID, g.client) - if flavour == nil { - http.Error(w, "Flavour not found", http.StatusNotFound) - return - } - - // Create a new transaction ID - transactionID, err := namings.ForgeTransactionID() - if err != nil { - http.Error(w, "Error generating transaction ID", http.StatusInternalServerError) - return - } - - // Create a new transaction - transaction := resourceforge.ForgeTransactionObj(transactionID, request) - - // Add the transaction to the transactions map - g.addNewTransacion(transaction) - } - - klog.Infof("Transaction %s reserved", transaction.TransactionID) - - encodeResponse(w, transaction) -} - -// purchaseFlavour is an handler for purchasing a Flavour -func (g *Gateway) purchaseFlavour(w http.ResponseWriter, r *http.Request) { - // Get the flavourID value from the URL parameters - params := mux.Vars(r) - transactionID := params["transactionID"] - var purchase models.PurchaseRequest - - if err := json.NewDecoder(r.Body).Decode(&purchase); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - if transactionID != purchase.TransactionID { - klog.Infof("Mismatch body & param") - http.Error(w, "Mismatch body & param", http.StatusConflict) - return - } - - klog.Infof("Purchasing request for transaction %s", purchase.TransactionID) - - // Retrieve the transaction from the transactions map - transaction, err := g.GetTransaction(purchase.TransactionID) - if err != nil { - klog.Errorf("Error getting the Transaction: %s", err) - http.Error(w, "Error getting the Transaction", http.StatusInternalServerError) - return - } - - klog.Infof("Flavour requested: %s", transaction.FlavourID) - - if tools.CheckExpiration(transaction.StartTime, flags.EXPIRATION_TRANSACTION) { - klog.Infof("Transaction %s expired", transaction.TransactionID) - http.Error(w, "Error: transaction Timeout", http.StatusRequestTimeout) - g.removeTransaction(transaction.TransactionID) - return - } - - var contractList reservationv1alpha1.ContractList - var contract reservationv1alpha1.Contract - - // Check if the Contract with the same TransactionID already exists - if err := g.client.List(context.Background(), &contractList, client.MatchingFields{"spec.transactionID": purchase.TransactionID}); err != nil { - if client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when listing Contracts: %s", err) - http.Error(w, "Error when listing Contracts", http.StatusInternalServerError) - return - } - } - - if len(contractList.Items) > 0 { - klog.Infof("Contract already exists for transaction %s", purchase.TransactionID) - contract = contractList.Items[0] - // Create a contract object to be returned with the response - contractObject := parseutil.ParseContract(&contract) - // create a response purchase - responsePurchase := resourceforge.ForgeResponsePurchaseObj(contractObject) - // Respond with the response purchase as JSON - encodeResponse(w, responsePurchase) - return - } - - klog.Infof("Performing purchase of flavour %s...", transaction.FlavourID) - - // Remove the transaction from the transactions map - g.removeTransaction(transaction.TransactionID) - - klog.Infof("Flavour %s successfully purchased!", transaction.FlavourID) - - // Get the flavour sold for creating the contract - flavourSold, err := services.GetFlavourByID(transaction.FlavourID, g.client) - if err != nil { - klog.Errorf("Error getting the Flavour by ID: %s", err) - http.Error(w, "Error getting the Flavour by ID", http.StatusInternalServerError) - return - } - - liqoCredentials, err := GetLiqoCredentials(context.Background(), g.client) - if err != nil { - klog.Errorf("Error getting Liqo Credentials: %s", err) - http.Error(w, "Error getting Liqo Credentials", http.StatusInternalServerError) - return - } - - // Create a new contract - klog.Infof("Creating a new contract...") - contract = *resourceforge.ForgeContract(*flavourSold, transaction, liqoCredentials) - err = g.client.Create(context.Background(), &contract) - if err != nil { - klog.Errorf("Error creating the Contract: %s", err) - http.Error(w, "Error creating the Contract: "+err.Error(), http.StatusInternalServerError) - return - } - - klog.Infof("Contract created!") - - // Create a contract object to be returned with the response - contractObject := parseutil.ParseContract(&contract) - // create a response purchase - responsePurchase := resourceforge.ForgeResponsePurchaseObj(contractObject) - - // Respond with the response purchase as JSON - encodeResponse(w, responsePurchase) -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "context" - "fmt" - "net/http" - "time" - - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/consts" - "github.com/fluidos-project/node/pkg/utils/flags" - "github.com/fluidos-project/node/pkg/utils/getters" - "github.com/fluidos-project/node/pkg/utils/models" - "github.com/fluidos-project/node/pkg/utils/tools" - "github.com/gorilla/mux" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// clusterRole -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=contracts,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=flavours,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=flavours/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=flavours/finalizers,verbs=update -//+kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch -//+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch - -const ( - LIST_FLAVOURS_PATH = "/api/listflavours" - LIST_FLAVOUR_BY_ID_PATH = "/api/listflavours/" - RESERVE_FLAVOUR_PATH = "/api/reserveflavour/" - PURCHASE_FLAVOUR_PATH = "/api/purchaseflavour/" - LIST_FLAVOURS_BY_SELECTOR_PATH = "/api/listflavours/selector" -) - -type Gateway struct { - // NodeIdentity is the identity of the FLUIDOS Node - ID *nodecorev1alpha1.NodeIdentity - - // Transactions is a map of Transaction - Transactions map[string]models.Transaction - - // client is the Kubernetes client - client client.Client - - // Readyness of the Gateway. It is set when liqo is installed - LiqoReady bool - - // The Liqo ClusterID - ClusterID string -} - -func NewGateway(c client.Client) *Gateway { - return &Gateway{ - client: c, - Transactions: make(map[string]models.Transaction), - LiqoReady: false, - ClusterID: "", - } -} - -// Start starts a new HTTP server -func (g *Gateway) Start(ctx context.Context) error { - klog.Info("Getting FLUIDOS Node identity...") - - nodeIdentity := getters.GetNodeIdentity(ctx, g.client) - if nodeIdentity == nil { - klog.Info("Error getting FLUIDOS Node identity") - return fmt.Errorf("Error getting FLUIDOS Node identity") - } - - g.RegisterNodeIdentity(nodeIdentity) - - router := mux.NewRouter() - - // middleware for debugging purposes - // router.Use(loggingMiddleware) - - // middleware for readiness - router.Use(g.readinessMiddleware) - - // Gateway endpoints - router.HandleFunc(LIST_FLAVOURS_PATH, g.getFlavours).Methods("GET") - //router.HandleFunc(LIST_FLAVOUR_BY_ID_PATH+"{flavourID}", g.getFlavourByID).Methods("GET") - router.HandleFunc(LIST_FLAVOURS_BY_SELECTOR_PATH, g.getFlavoursBySelector).Methods("POST") - router.HandleFunc(RESERVE_FLAVOUR_PATH+"{flavourID}", g.reserveFlavour).Methods("POST") - router.HandleFunc(PURCHASE_FLAVOUR_PATH+"{transactionID}", g.purchaseFlavour).Methods("POST") - - // Configure the HTTP server - srv := &http.Server{ - Handler: router, - Addr: ":" + flags.HTTP_PORT, - } - - // Start server HTTP - klog.Infof("Starting HTTP server on port %s", flags.HTTP_PORT) - return srv.ListenAndServe() -} - -func (g *Gateway) RegisterNodeIdentity(nodeIdentity *nodecorev1alpha1.NodeIdentity) { - g.ID = nodeIdentity -} - -// Only for debugging purposes -/* func loggingMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - klog.Infof("Received request: %s %s %v", r.Method, r.URL.Path, r) - next.ServeHTTP(w, r) - }) -} */ - -func (g *Gateway) readinessMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !g.LiqoReady { - klog.Infof("Liqo not ready yet") - w.WriteHeader(http.StatusServiceUnavailable) - return - } - next.ServeHTTP(w, r) - }) -} - -func (g *Gateway) CacheRefresher(interval time.Duration) func(ctx context.Context) error { - return func(ctx context.Context) error { - return wait.PollUntilContextCancel(ctx, interval, false, g.refreshCache) - } -} - -// check expired transactions and remove them from the cache -func (g *Gateway) refreshCache(ctx context.Context) (bool, error) { - klog.Infof("Refreshing cache") - for transactionID, transaction := range g.Transactions { - if tools.CheckExpiration(transaction.StartTime, flags.EXPIRATION_TRANSACTION) { - klog.Infof("Transaction %s expired, removing it from cache...", transactionID) - g.removeTransaction(transactionID) - return false, nil - } - } - return false, nil -} - -func (g *Gateway) LiqoChecker(interval time.Duration) func(ctx context.Context) error { - return func(ctx context.Context) error { - return wait.PollUntilContextCancel(ctx, interval, false, g.checkLiqoReadiness) - } -} - -func (g *Gateway) checkLiqoReadiness(ctx context.Context) (bool, error) { - klog.Infof("Checking Liqo readiness") - if g.LiqoReady && g.ClusterID != "" { - return true, nil - } - - var cm corev1.ConfigMap - err := g.client.Get(ctx, types.NamespacedName{Name: consts.LIQO_CLUSTERID_CONFIGMAP_NAME, Namespace: consts.LIQO_NAMESPACE}, &cm) - if err != nil { - if client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when retrieving Liqo ConfigMap: %s", err) - } - klog.Infof("Liqo not ready yet. ConfigMap not found") - return false, nil - } - - if cm.Data["CLUSTER_ID"] != "" && cm.Data["CLUSTER_NAME"] != "" { - klog.Infof("Liqo is ready") - g.LiqoReady = true - g.ClusterID = cm.Data["CLUSTER_ID"] - return true, nil - } - - klog.Infof("Liqo not ready yet") - return false, nil -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/models" - "github.com/fluidos-project/node/pkg/utils/resourceforge" - "k8s.io/klog/v2" -) - -func searchFlavourWithSelector(selector *models.Selector, addr string) (*nodecorev1alpha1.Flavour, error) { - var flavour models.Flavour - - // Marshal the selector into JSON bytes - selectorBytes, err := json.Marshal(selector) - if err != nil { - return nil, err - } - - body := bytes.NewBuffer(selectorBytes) - url := fmt.Sprintf("http://%s%s", addr, LIST_FLAVOURS_BY_SELECTOR_PATH) - - resp, err := makeRequest("POST", url, body) - if err != nil { - return nil, err - } - - // Check if the response status code is 200 (OK) - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("received non-OK response status code: %d", resp.StatusCode) - } - - if err := json.NewDecoder(resp.Body).Decode(&flavour); err != nil { - klog.Errorf("Error decoding the response body: %s", err) - return nil, err - } - - flavourCR := resourceforge.ForgeFlavourFromObj(flavour) - - return flavourCR, nil -} - -func searchFlavour(addr string) (*nodecorev1alpha1.Flavour, error) { - var flavour models.Flavour - - url := fmt.Sprintf("http://%s%s", addr, LIST_FLAVOURS_PATH) - - resp, err := makeRequest("GET", url, nil) - if err != nil { - return nil, err - } - - // Check if the response status code is 200 (OK) - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("received non-OK response status code: %d", resp.StatusCode) - } - - if err := json.NewDecoder(resp.Body).Decode(&flavour); err != nil { - klog.Errorf("Error decoding the response body: %s", err) - return nil, err - } - - flavourCR := resourceforge.ForgeFlavourFromObj(flavour) - - return flavourCR, nil -} - -func makeRequest(method, url string, body *bytes.Buffer) (*http.Response, error) { - - httpClient := &http.Client{} - - if body == nil { - body = bytes.NewBuffer([]byte{}) - } - - req, err := http.NewRequest(method, url, body) - if err != nil { - klog.Errorf("Error creating the request: %s", err) - return nil, err - } - req.Close = true - - if method == "POST" { - req.Header.Set("Content-Type", "application/json") - } - - resp, err := httpClient.Do(req) - if err != nil { - klog.Errorf("Error sending the request: %s", err.Error()) - return nil, err - } - - return resp, nil -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/getters" - "github.com/fluidos-project/node/pkg/utils/models" - "github.com/fluidos-project/node/pkg/utils/parseutil" - "k8s.io/klog/v2" -) - -// TODO: move this function into the REAR Gateway package -// reserveFlavour reserves a flavour with the given flavourID -func (g *Gateway) ReserveFlavour(ctx context.Context, reservation *reservationv1alpha1.Reservation, flavourID string) (*models.Transaction, error) { - err := checkLiqoReadiness(g.LiqoReady) - if err != nil { - return nil, err - } - - var transaction models.Transaction - - body := models.ReserveRequest{ - FlavourID: flavourID, - Buyer: models.NodeIdentity{ - NodeID: g.ID.NodeID, - IP: g.ID.IP, - Domain: g.ID.Domain, - }, - } - - klog.Infof("Reservation %s for flavour %s", reservation.Name, flavourID) - - if reservation.Spec.Partition != nil { - body.Partition = parseutil.ParsePartition(reservation.Spec.Partition) - } - - selectorBytes, err := json.Marshal(body) - if err != nil { - return nil, err - } - - // TODO: this url should be taken from the nodeIdentity of the flavour - bodyBytes := bytes.NewBuffer(selectorBytes) - url := fmt.Sprintf("http://%s%s%s", reservation.Spec.Seller.IP, RESERVE_FLAVOUR_PATH, flavourID) - - klog.Infof("Sending request to %s", url) - - resp, err := makeRequest("POST", url, bodyBytes) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - // Check if the response status code is 200 (OK) - if resp.StatusCode != http.StatusOK { - klog.Errorf("Received non-OK response status code: %v", resp) - return nil, fmt.Errorf("received non-OK response status code: %d", resp.StatusCode) - } - - if err := json.NewDecoder(resp.Body).Decode(&transaction); err != nil { - return nil, err - } - - // check if transaction is not correctly set - // This behaviuor is a possible bug of the rear-controller - // TODO: check it - if transaction.TransactionID == "" { - klog.Errorf("TransactionID received is empty") - return &transaction, fmt.Errorf("transactionID is empty") - } - - klog.Infof("Flavour %s reserved: transaction ID %s", flavourID, transaction.TransactionID) - - g.addNewTransacion(transaction) - - return &transaction, nil -} - -// PurchaseFlavour purchases a flavour with the given flavourID -func (g *Gateway) PurchaseFlavour(ctx context.Context, transactionID string, seller nodecorev1alpha1.NodeIdentity) (*models.ResponsePurchase, error) { - err := checkLiqoReadiness(g.LiqoReady) - if err != nil { - return nil, err - } - - var purchase models.ResponsePurchase - - // Check if the transaction exists - transaction, err := g.GetTransaction(transactionID) - if err != nil { - return nil, err - } - - body := models.PurchaseRequest{ - TransactionID: transaction.TransactionID, - } - - selectorBytes, err := json.Marshal(body) - if err != nil { - return nil, err - } - - bodyBytes := bytes.NewBuffer(selectorBytes) - // TODO: this url should be taken from the nodeIdentity of the flavour - url := fmt.Sprintf("http://%s%s%s", seller.IP, PURCHASE_FLAVOUR_PATH, transactionID) - - resp, err := makeRequest("POST", url, bodyBytes) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - // Check if the response status code is 200 (OK) - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("received non-OK response status code: %d", resp.StatusCode) - } - - if err := json.NewDecoder(resp.Body).Decode(&purchase); err != nil { - return nil, err - } - - return &purchase, nil -} - -// SearchFlavour is a function that returns an array of Flavour that fit the Selector by performing a get request to an http server -func (g *Gateway) DiscoverFlavours(selector *nodecorev1alpha1.FlavourSelector) ([]*nodecorev1alpha1.Flavour, error) { - err := checkLiqoReadiness(g.LiqoReady) - if err != nil { - return nil, err - } - - var s *models.Selector - var flavoursCR []*nodecorev1alpha1.Flavour - - if selector == nil { - s = parseutil.ParseFlavourSelector(selector) - } - - providers := getters.GetLocalProviders(context.Background(), g.client) - - // Send the POST request to all the servers in the list - for _, provider := range providers { - flavour, err := discover(s, provider) - if err != nil { - klog.Errorf("Error when searching Flavour: %s", err) - return nil, err - } - flavoursCR = append(flavoursCR, flavour) - } - - klog.Infof("Found %d flavours", len(flavoursCR)) - return flavoursCR, nil -} - -func discover(s *models.Selector, provider string) (*nodecorev1alpha1.Flavour, error) { - if s != nil { - return searchFlavourWithSelector(s, provider) - } - return searchFlavour(provider) -} - -func checkLiqoReadiness(b bool) error { - if !b { - klog.Errorf("Liqo is not ready, please check or wait for the Liqo installation") - return fmt.Errorf("Liqo is not ready, please check or wait for the Liqo installation") - } - return nil -} -skip file ./pkg/rear-controller/gateway/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - - reservationsv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/models" - "github.com/liqotech/liqo/pkg/auth" - "github.com/liqotech/liqo/pkg/utils" - foreigncluster "github.com/liqotech/liqo/pkg/utils/foreignCluster" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// buildSelector builds a selector from a request body -func buildSelector(body []byte) (*models.Selector, error) { - // Parse the request body into the APIRequest struct - var selector models.Selector - err := json.Unmarshal(body, &selector) - if err != nil { - return &models.Selector{}, err - } - return &selector, nil -} - -// getTransaction returns a transaction from the transactions map -func (g *Gateway) GetTransaction(transactionID string) (models.Transaction, error) { - transaction, exists := g.Transactions[transactionID] - if !exists { - return models.Transaction{}, fmt.Errorf("Transaction not found") - } - return transaction, nil -} - -// SearchTransaction returns a transaction from the transactions map -func (g *Gateway) SearchTransaction(buyerID string, flavourID string) (models.Transaction, bool) { - for _, t := range g.Transactions { - if t.Buyer.NodeID == buyerID && t.FlavourID == flavourID { - return t, true - } - } - return models.Transaction{}, false -} - -// addNewTransacion add a new transaction to the transactions map -func (g *Gateway) addNewTransacion(transaction models.Transaction) { - g.Transactions[transaction.TransactionID] = transaction -} - -// removeTransaction removes a transaction from the transactions map -func (g *Gateway) removeTransaction(transactionID string) { - delete(g.Transactions, transactionID) -} - -// handleError handles errors by sending an error response -func handleError(w http.ResponseWriter, err error, statusCode int) { - http.Error(w, err.Error(), statusCode) -} - -// encodeResponse encodes the response as JSON and writes it to the response writer -func encodeResponse(w http.ResponseWriter, data interface{}) { - - resp, err := json.Marshal(data) - if err != nil { - handleError(w, err, http.StatusInternalServerError) - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(resp) -} - -const ( - //authTokenSecretNamePrefix = "remote-token-" - - //tokenKey = "token" - - liqoNamespace = "liqo" -) - -func GetLiqoCredentials(ctx context.Context, cl client.Client) (*reservationsv1alpha1.LiqoCredentials, error) { - localToken, err := auth.GetToken(ctx, cl, liqoNamespace) - if err != nil { - return nil, err - } - - clusterIdentity, err := utils.GetClusterIdentityWithControllerClient(ctx, cl, liqoNamespace) - if err != nil { - return nil, err - } - - authEP, err := foreigncluster.GetHomeAuthURL(ctx, cl, liqoNamespace) - if err != nil { - return nil, err - } - - // If the local cluster has not a cluster name, we print the use the local clusterID to not leave this field empty. - // This can be changed by the user when pasting this value in a remote cluster. - if clusterIdentity.ClusterName == "" { - clusterIdentity.ClusterName = clusterIdentity.ClusterID - } - - return &reservationsv1alpha1.LiqoCredentials{ - ClusterName: clusterIdentity.ClusterName, - ClusterID: clusterIdentity.ClusterID, - Endpoint: authEP, - Token: localToken, - }, nil -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tools - -import ( - "time" - - "k8s.io/klog/v2" -) - -// GetTimeNow returns the current time in RFC3339 format -func GetTimeNow() string { - return time.Now().Format(time.RFC3339) -} - -// CheckExpiration checks if the timestamp has expired -func CheckExpiration(timestamp string, expTime time.Duration) bool { - t, err := time.Parse(time.RFC3339, timestamp) - if err != nil { - klog.Errorf("Error parsing the transaction start time: %s", err) - return false - } - return time.Since(t) > expTime -} -skip file ./pkg/utils/flags/flags.go since no import -skip file ./pkg/utils/flags/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package models - -import ( - "time" - - "k8s.io/apimachinery/pkg/api/resource" -) - -// Flavour represents a Flavour object with its characteristics and policies. -type Flavour struct { - FlavourID string `json:"flavourID"` - ProviderID string `json:"providerID"` - Type string `json:"type"` - Characteristics Characteristics `json:"characteristics"` - Policy Policy `json:"policy"` - Owner NodeIdentity `json:"owner"` - Price Price `json:"price"` - ExpirationTime time.Time `json:"expirationTime"` - OptionalFields OptionalFields `json:"optionalFields"` -} - -// Characteristics represents the characteristics of a Flavour, such as CPU and RAM. -type Characteristics struct { - CPU resource.Quantity `json:"cpu,omitempty"` - Memory resource.Quantity `json:"memory,omitempty"` - PersistentStorage resource.Quantity `json:"storage,omitempty"` - EphemeralStorage resource.Quantity `json:"ephemeralStorage,omitempty"` - Gpu resource.Quantity `json:"gpu,omitempty"` - Architecture string `json:"architecture,omitempty"` -} - -// Policy represents the policy associated with a Flavour, which can be either Partitionable or Aggregatable. -type Policy struct { - Partitionable *Partitionable `json:"partitionable,omitempty"` - Aggregatable *Aggregatable `json:"aggregatable,omitempty"` -} - -// Partitionable represents the partitioning properties of a Flavour, such as the minimum and incremental values of CPU and RAM. -type Partitionable struct { - CPUMinimum resource.Quantity `json:"cpuMinimum"` - MemoryMinimum resource.Quantity `json:"memoryMinimum"` - CPUStep resource.Quantity `json:"cpuStep"` - MemoryStep resource.Quantity `json:"memoryStep"` -} - -// Aggregatable represents the aggregation properties of a Flavour, such as the minimum instance count. -type Aggregatable struct { - MinCount int `json:"minCount"` - MaxCount int `json:"maxCount"` -} - -// NodeIdentity represents the owner of a Flavour, with associated ID, IP, and domain name. -type NodeIdentity struct { - NodeID string `json:"ID"` - IP string `json:"IP"` - Domain string `json:"domain"` -} - -// Price represents the price of a Flavour, with the amount, currency, and period associated. -type Price struct { - Amount string `json:"amount"` - Currency string `json:"currency"` - Period string `json:"period"` -} - -// OptionalFields represents the optional fields of a Flavour, such as availability. -type OptionalFields struct { - Availability bool `json:"availability,omitempty"` - WorkerID string `json:"workerID"` -} - -type Selector struct { - FlavourType string `json:"type,omitempty"` - Architecture string `json:"architecture,omitempty"` - RangeSelector *RangeSelector `json:"rangeSelector,omitempty"` - MatchSelector *MatchSelector `json:"matchSelector,omitempty"` -} - -// MatchSelector represents the criteria for selecting Flavours through a strict match. -type MatchSelector struct { - Cpu resource.Quantity `json:"cpu,omitempty"` - Memory resource.Quantity `json:"memory,omitempty"` - Storage resource.Quantity `json:"storage,omitempty"` - EphemeralStorage resource.Quantity `json:"ephemeralStorage,omitempty"` - Gpu resource.Quantity `json:"gpu,omitempty"` -} - -// RangeSelector represents the criteria for selecting Flavours through a range. -type RangeSelector struct { - MinCpu resource.Quantity `json:"minCpu,omitempty"` - MinMemory resource.Quantity `json:"minMemory,omitempty"` - MinStorage resource.Quantity `json:"minStorage,omitempty"` - MinEph resource.Quantity `json:"minEph,omitempty"` - MinGpu resource.Quantity `json:"minGpu,omitempty"` - MaxCpu resource.Quantity `json:"maxCpu,omitempty"` - MaxMemory resource.Quantity `json:"maxMemory,omitempty"` - MaxStorage resource.Quantity `json:"maxStorage,omitempty"` - MaxEph resource.Quantity `json:"maxEph,omitempty"` - MaxGpu resource.Quantity `json:"maxGpu,omitempty"` -} -skip file ./pkg/utils/models/gateway.go since no import -skip file ./pkg/utils/models/reservation.go since no import -skip file ./pkg/utils/models/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package models - -import ( - "k8s.io/apimachinery/pkg/api/resource" -) - -// NodeInfo represents a node and its resources -type NodeInfo struct { - UID string `json:"uid"` - Name string `json:"name"` - Architecture string `json:"architecture"` - OperatingSystem string `json:"os"` - ResourceMetrics ResourceMetrics `json:"resources"` -} - -// ResourceMetrics represents resources of a certain node -type ResourceMetrics struct { - CPUTotal resource.Quantity `json:"totalCPU"` - CPUAvailable resource.Quantity `json:"availableCPU"` - MemoryTotal resource.Quantity `json:"totalMemory"` - MemoryAvailable resource.Quantity `json:"availableMemory"` - EphemeralStorage resource.Quantity `json:"ephemeralStorage"` -} -skip file ./pkg/utils/parseutil/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package parseutil - -import ( - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/models" - "k8s.io/apimachinery/pkg/api/resource" -) - -// ParseFlavourSelector parses FlavourSelector into a Selector -func ParseFlavourSelector(selector *nodecorev1alpha1.FlavourSelector) (s *models.Selector) { - - s.Architecture = selector.Architecture - s.FlavourType = string(selector.FlavourType) - - if selector.MatchSelector != nil { - s.MatchSelector = &models.MatchSelector{ - Cpu: selector.MatchSelector.Cpu, - Memory: selector.MatchSelector.Memory, - EphemeralStorage: selector.MatchSelector.EphemeralStorage, - Storage: selector.MatchSelector.Storage, - Gpu: selector.MatchSelector.Gpu, - } - } - - if selector.RangeSelector != nil { - s.RangeSelector = &models.RangeSelector{ - MinCpu: selector.RangeSelector.MinCpu, - MinMemory: selector.RangeSelector.MinMemory, - MinEph: selector.RangeSelector.MinEph, - MinStorage: selector.RangeSelector.MinStorage, - MinGpu: selector.RangeSelector.MinGpu, - MaxCpu: selector.RangeSelector.MaxCpu, - MaxMemory: selector.RangeSelector.MaxMemory, - MaxEph: selector.RangeSelector.MaxEph, - MaxStorage: selector.RangeSelector.MaxStorage, - MaxGpu: selector.RangeSelector.MaxGpu, - } - } - - return -} - -func ParsePartition(partition *reservationv1alpha1.Partition) *models.Partition { - return &models.Partition{ - Cpu: partition.Cpu, - Memory: partition.Memory, - EphemeralStorage: partition.EphemeralStorage, - Storage: partition.Storage, - Gpu: partition.Gpu, - } -} - -func ParsePartitionFromObj(partition *models.Partition) *reservationv1alpha1.Partition { - return &reservationv1alpha1.Partition{ - Architecture: partition.Architecture, - Cpu: partition.Cpu, - Memory: partition.Memory, - Gpu: partition.Gpu, - Storage: partition.Storage, - EphemeralStorage: partition.EphemeralStorage, - } -} - -func ParseNodeIdentity(node nodecorev1alpha1.NodeIdentity) models.NodeIdentity { - return models.NodeIdentity{ - NodeID: node.NodeID, - IP: node.IP, - Domain: node.Domain, - } -} - -// ParseFlavourObject creates a Flavour Object from a Flavour CR -func ParseFlavour(flavour nodecorev1alpha1.Flavour) models.Flavour { - return models.Flavour{ - FlavourID: flavour.Name, - Type: string(flavour.Spec.Type), - ProviderID: flavour.Spec.ProviderID, - Characteristics: models.Characteristics{ - CPU: flavour.Spec.Characteristics.Cpu, - Memory: flavour.Spec.Characteristics.Memory, - PersistentStorage: flavour.Spec.Characteristics.PersistentStorage, - EphemeralStorage: flavour.Spec.Characteristics.EphemeralStorage, - Gpu: flavour.Spec.Characteristics.Gpu, - }, - Owner: ParseNodeIdentity(flavour.Spec.Owner), - Policy: models.Policy{ - Partitionable: func() *models.Partitionable { - if flavour.Spec.Policy.Partitionable != nil { - return &models.Partitionable{ - CPUMinimum: flavour.Spec.Policy.Partitionable.CpuMin, - MemoryMinimum: flavour.Spec.Policy.Partitionable.MemoryMin, - CPUStep: flavour.Spec.Policy.Partitionable.CpuStep, - MemoryStep: flavour.Spec.Policy.Partitionable.MemoryStep, - } - } - return nil - }(), - Aggregatable: func() *models.Aggregatable { - if flavour.Spec.Policy.Aggregatable != nil { - return &models.Aggregatable{ - MinCount: flavour.Spec.Policy.Aggregatable.MinCount, - MaxCount: flavour.Spec.Policy.Aggregatable.MaxCount, - } - } - return nil - }(), - }, - Price: models.Price{ - Amount: flavour.Spec.Price.Amount, - Currency: flavour.Spec.Price.Currency, - Period: flavour.Spec.Price.Period, - }, - OptionalFields: models.OptionalFields{ - Availability: flavour.Spec.OptionalFields.Availability, - WorkerID: flavour.Spec.OptionalFields.WorkerID, - }, - } -} - -// ForgeContractObject creates a Contract Object -func ParseContract(contract *reservationv1alpha1.Contract) models.Contract { - return models.Contract{ - ContractID: contract.Name, - Flavour: ParseFlavour(contract.Spec.Flavour), - Buyer: ParseNodeIdentity(contract.Spec.Buyer), - BuyerClusterID: contract.Spec.BuyerClusterID, - TransactionID: contract.Spec.TransactionID, - Partition: ParsePartition(contract.Spec.Partition), - Seller: ParseNodeIdentity(contract.Spec.Seller), - SellerCredentials: models.LiqoCredentials{ - ClusterID: contract.Spec.SellerCredentials.ClusterID, - ClusterName: contract.Spec.SellerCredentials.ClusterName, - Token: contract.Spec.SellerCredentials.Token, - Endpoint: contract.Spec.SellerCredentials.Endpoint, - }, - } -} - -func ParseQuantityFromString(s string) resource.Quantity { - i, err := resource.ParseQuantity(s) - if err != nil { - return *resource.NewQuantity(0, resource.DecimalSI) - } - return i -} -skip file ./pkg/utils/common/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package common - -import ( - "fmt" - - advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/models" - "github.com/fluidos-project/node/pkg/utils/namings" - "github.com/fluidos-project/node/pkg/utils/parseutil" - "k8s.io/klog/v2" -) - -// FilterFlavoursBySelector returns the Flavour CRs in the cluster that match the selector -func FilterFlavoursBySelector(flavours []nodecorev1alpha1.Flavour, selector *models.Selector) ([]nodecorev1alpha1.Flavour, error) { - var flavoursSelected []nodecorev1alpha1.Flavour - - // Get the Flavours that match the selector - for _, f := range flavours { - if string(f.Spec.Type) == selector.FlavourType { - // filter function - if FilterFlavour(selector, f) { - flavoursSelected = append(flavoursSelected, f) - } - - } - } - - return flavoursSelected, nil -} - -// filterFlavour filters the Flavour CRs in the cluster that match the selector -func FilterFlavour(selector *models.Selector, f nodecorev1alpha1.Flavour) bool { - - if f.Spec.Characteristics.Architecture != selector.Architecture { - klog.Infof("Flavour %s has different architecture: %s - Selector: %s", f.Name, f.Spec.Characteristics.Architecture, selector.Architecture) - return false - } - - if selector.MatchSelector != nil { - if selector.MatchSelector.Cpu.CmpInt64(0) == 0 && f.Spec.Characteristics.Cpu.Cmp(selector.MatchSelector.Cpu) != 0 { - klog.Infof("MatchSelector Cpu: %d - Flavour Cpu: %d", selector.MatchSelector.Cpu, f.Spec.Characteristics.Cpu) - return false - } - - if selector.MatchSelector.Memory.CmpInt64(0) == 0 && f.Spec.Characteristics.Memory.Cmp(selector.MatchSelector.Memory) != 0 { - klog.Infof("MatchSelector Memory: %d - Flavour Memory: %d", selector.MatchSelector.Memory, f.Spec.Characteristics.Memory) - return false - } - - if selector.MatchSelector.EphemeralStorage.CmpInt64(0) == 0 && f.Spec.Characteristics.EphemeralStorage.Cmp(selector.MatchSelector.EphemeralStorage) != 0 { - klog.Infof("MatchSelector EphemeralStorage: %d - Flavour EphemeralStorage: %d", selector.MatchSelector.EphemeralStorage, f.Spec.Characteristics.EphemeralStorage) - return false - } - - if selector.MatchSelector.Storage.CmpInt64(0) == 0 && f.Spec.Characteristics.PersistentStorage.Cmp(selector.MatchSelector.Storage) != 0 { - klog.Infof("MatchSelector Storage: %d - Flavour Storage: %d", selector.MatchSelector.Storage, f.Spec.Characteristics.PersistentStorage) - return false - } - - if selector.MatchSelector.Gpu.CmpInt64(0) == 0 && f.Spec.Characteristics.Gpu.Cmp(selector.MatchSelector.Gpu) != 0 { - klog.Infof("MatchSelector GPU: %d - Flavour GPU: %d", selector.MatchSelector.Gpu, f.Spec.Characteristics.Gpu) - return false - } - } - - if selector.RangeSelector != nil && selector.MatchSelector == nil { - - if selector.RangeSelector.MinCpu.CmpInt64(0) != 0 && f.Spec.Characteristics.Cpu.Cmp(selector.RangeSelector.MinCpu) < 0 { - klog.Infof("RangeSelector MinCpu: %d - Flavour Cpu: %d", selector.RangeSelector.MinCpu, f.Spec.Characteristics.Cpu) - return false - } - - if selector.RangeSelector.MinMemory.CmpInt64(0) != 0 && f.Spec.Characteristics.Memory.Cmp(selector.RangeSelector.MinMemory) < 0 { - klog.Infof("RangeSelector MinMemory: %d - Flavour Memory: %d", selector.RangeSelector.MinMemory, f.Spec.Characteristics.Memory) - return false - } - - if selector.RangeSelector.MinEph.CmpInt64(0) != 0 && f.Spec.Characteristics.EphemeralStorage.Cmp(selector.RangeSelector.MinEph) < 0 { - klog.Infof("RangeSelector MinEph: %d - Flavour EphemeralStorage: %d", selector.RangeSelector.MinEph, f.Spec.Characteristics.EphemeralStorage) - return false - } - - if selector.RangeSelector.MinStorage.CmpInt64(0) != 0 && f.Spec.Characteristics.PersistentStorage.Cmp(selector.RangeSelector.MinStorage) < 0 { - klog.Infof("RangeSelector MinStorage: %d - Flavour Storage: %d", selector.RangeSelector.MinStorage, f.Spec.Characteristics.PersistentStorage) - return false - } - - if selector.RangeSelector.MinGpu.CmpInt64(0) != 0 && f.Spec.Characteristics.Gpu.Cmp(selector.RangeSelector.MinGpu) < 0 { - return false - } - - if selector.RangeSelector.MaxCpu.CmpInt64(0) != 0 && f.Spec.Characteristics.Cpu.Cmp(selector.RangeSelector.MaxCpu) > 0 { - return false - } - - if selector.RangeSelector.MaxMemory.CmpInt64(0) != 0 && f.Spec.Characteristics.Memory.Cmp(selector.RangeSelector.MaxMemory) > 0 { - return false - } - - if selector.RangeSelector.MaxEph.CmpInt64(0) != 0 && f.Spec.Characteristics.EphemeralStorage.Cmp(selector.RangeSelector.MaxEph) > 0 { - return false - } - - if selector.RangeSelector.MaxStorage.CmpInt64(0) != 0 && f.Spec.Characteristics.PersistentStorage.Cmp(selector.RangeSelector.MaxStorage) > 0 { - return false - } - - if selector.RangeSelector.MaxGpu.CmpInt64(0) != 0 && f.Spec.Characteristics.Gpu.Cmp(selector.RangeSelector.MaxGpu) > 0 { - return false - } - } - - return true -} - -// FilterPeeringCandidate filters the peering candidate based on the solver's flavour selector -func FilterPeeringCandidate(selector *nodecorev1alpha1.FlavourSelector, pc *advertisementv1alpha1.PeeringCandidate) bool { - s := parseutil.ParseFlavourSelector(selector) - return FilterFlavour(s, pc.Spec.Flavour) -} - -// CheckSelector ia a func to check if the syntax of the Selector is right. -// Strict and range syntax cannot be used together -func CheckSelector(selector *models.Selector) error { - - if selector.MatchSelector != nil && selector.RangeSelector != nil { - return fmt.Errorf("selector syntax error: strict and range syntax cannot be used together") - } - return nil -} - -// SOLVER PHASE SETTERS - -// DiscoveryStatusCheck checks the status of the discovery -func DiscoveryStatusCheck(solver *nodecorev1alpha1.Solver, discovery *advertisementv1alpha1.Discovery) { - if discovery.Status.Phase.Phase == nodecorev1alpha1.PhaseSolved { - klog.Infof("Discovery %s has found a candidate: %s", discovery.Name, discovery.Status.PeeringCandidate) - solver.Status.FindCandidate = nodecorev1alpha1.PhaseSolved - solver.Status.PeeringCandidate = discovery.Status.PeeringCandidate - solver.Status.DiscoveryPhase = nodecorev1alpha1.PhaseSolved - solver.SetPhase(nodecorev1alpha1.PhaseRunning, "Solver has found a candidate") - } - if discovery.Status.Phase.Phase == nodecorev1alpha1.PhaseFailed { - klog.Infof("Discovery %s has failed. Reason: %s", discovery.Name, discovery.Status.Phase.Message) - klog.Infof("Peering candidate not found, Solver %s failed", solver.Name) - solver.Status.FindCandidate = nodecorev1alpha1.PhaseFailed - solver.Status.DiscoveryPhase = nodecorev1alpha1.PhaseFailed - } - if discovery.Status.Phase.Phase == nodecorev1alpha1.PhaseTimeout { - klog.Infof("Discovery %s has timed out", discovery.Name) - solver.Status.FindCandidate = nodecorev1alpha1.PhaseTimeout - solver.Status.DiscoveryPhase = nodecorev1alpha1.PhaseTimeout - solver.SetPhase(nodecorev1alpha1.PhaseTimeout, "Discovery has expired before finding a candidate") - } - if discovery.Status.Phase.Phase == nodecorev1alpha1.PhaseRunning { - klog.Infof("Discovery %s is running", discovery.Name) - solver.SetDiscoveryStatus(nodecorev1alpha1.PhaseRunning) - } - if discovery.Status.Phase.Phase == nodecorev1alpha1.PhaseIdle { - klog.Infof("Discovery %s is idle", discovery.Name) - solver.SetDiscoveryStatus(nodecorev1alpha1.PhaseIdle) - } -} - -func ReservationStatusCheck(solver *nodecorev1alpha1.Solver, reservation *reservationv1alpha1.Reservation) { - klog.Infof("Reservation %s is in phase %s", reservation.Name, reservation.Status.Phase.Phase) - flavourName := namings.RetrieveFlavourNameFromPC(reservation.Spec.PeeringCandidate.Name) - if reservation.Status.Phase.Phase == nodecorev1alpha1.PhaseSolved { - klog.Infof("Reservation %s has reserved and purchase the flavour %s", reservation.Name, flavourName) - solver.Status.ReservationPhase = nodecorev1alpha1.PhaseSolved - solver.Status.ReserveAndBuy = nodecorev1alpha1.PhaseSolved - solver.SetPhase(nodecorev1alpha1.PhaseRunning, "Reservation: Flavour reserved and purchased") - } - if reservation.Status.Phase.Phase == nodecorev1alpha1.PhaseFailed { - klog.Infof("Reservation %s has failed. Reason: %s", reservation.Name, reservation.Status.Phase.Message) - solver.Status.ReservationPhase = nodecorev1alpha1.PhaseFailed - solver.Status.ReserveAndBuy = nodecorev1alpha1.PhaseFailed - solver.SetPhase(nodecorev1alpha1.PhaseFailed, "Reservation: Flavour reservation and purchase failed") - } - if reservation.Status.Phase.Phase == nodecorev1alpha1.PhaseRunning { - if reservation.Status.ReservePhase == nodecorev1alpha1.PhaseRunning { - klog.Infof("Reservation %s is running", reservation.Name) - solver.SetPhase(nodecorev1alpha1.PhaseRunning, "Reservation: Reserve is running") - } - if reservation.Status.PurchasePhase == nodecorev1alpha1.PhaseRunning { - klog.Infof("Purchasing %s is running", reservation.Name) - solver.SetPhase(nodecorev1alpha1.PhaseRunning, "Reservation: Purchase is running") - } - } - if reservation.Status.Phase.Phase == nodecorev1alpha1.PhaseIdle { - klog.Infof("Reservation %s is idle", reservation.Name) - solver.SetReservationStatus(nodecorev1alpha1.PhaseIdle) - } -} -skip file ./pkg/utils/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package getters - -import ( - "context" - "strings" - - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/consts" - "github.com/fluidos-project/node/pkg/utils/flags" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func GetNodeIdentity(ctx context.Context, cl client.Client) *nodecorev1alpha1.NodeIdentity { - - cm := &corev1.ConfigMap{} - - // Get the node identity - err := cl.Get(ctx, types.NamespacedName{ - Name: consts.NODE_IDENTITY_CONFIG_MAP_NAME, - Namespace: flags.FLUIDOS_NAMESPACE, - }, cm) - if err != nil { - klog.Errorf("Error getting the configmap: %s", err) - return nil - } - - return &nodecorev1alpha1.NodeIdentity{ - NodeID: cm.Data["nodeID"], - Domain: cm.Data["domain"], - IP: cm.Data["ip"], - } -} - -// This function retrieves the list of local providers ip addresses from the Network Manager configMap -func GetLocalProviders(ctx context.Context, cl client.Client) []string { - cm := &corev1.ConfigMap{} - - // Get the configmap - err := cl.Get(ctx, types.NamespacedName{ - Name: consts.NETWORK_CONFIG_MAP_NAME, - Namespace: flags.FLUIDOS_NAMESPACE, - }, cm) - if err != nil { - klog.Errorf("Error getting the configmap: %s", err) - return nil - } - return strings.Split(cm.Data["local"], ",") -} -skip file ./pkg/utils/consts/consts.go since no import -skip file ./pkg/utils/consts/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package services - -import ( - "context" - - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/flags" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// GetAllFlavours returns all the Flavours in the cluster -func GetAllFlavours(cl client.Client) ([]nodecorev1alpha1.Flavour, error) { - - var flavourList nodecorev1alpha1.FlavourList - - // List all Flavour CRs - err := cl.List(context.Background(), &flavourList) - if err != nil { - klog.Errorf("Error when listing Flavours: %s", err) - return nil, err - } - - return flavourList.Items, nil -} - -// GetFlavourByID returns the entire Flavour CR (not only spec) in the cluster that matches the flavourID -func GetFlavourByID(flavourID string, cl client.Client) (*nodecorev1alpha1.Flavour, error) { - - // Get the flavour with the given ID (that is the name of the CR) - flavour := &nodecorev1alpha1.Flavour{} - err := cl.Get(context.Background(), client.ObjectKey{ - Namespace: flags.FLUIDOS_NAMESPACE, - Name: flavourID, - }, flavour) - if err != nil { - klog.Errorf("Error when getting Flavour %s: %s", flavourID, err) - return nil, err - } - - return flavour, nil -} -skip file ./pkg/utils/services/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resourceforge - -import ( - "time" - - advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/flags" - "github.com/fluidos-project/node/pkg/utils/models" - "github.com/fluidos-project/node/pkg/utils/namings" - "github.com/fluidos-project/node/pkg/utils/parseutil" - "github.com/fluidos-project/node/pkg/utils/tools" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// ForgeDiscovery creates a Discovery CR from a FlavourSelector and a solverID -func ForgeDiscovery(selector *nodecorev1alpha1.FlavourSelector, solverID string) *advertisementv1alpha1.Discovery { - return &advertisementv1alpha1.Discovery{ - ObjectMeta: metav1.ObjectMeta{ - Name: namings.ForgeDiscoveryName(solverID), - Namespace: flags.FLUIDOS_NAMESPACE, - }, - Spec: advertisementv1alpha1.DiscoverySpec{ - Selector: func() *nodecorev1alpha1.FlavourSelector { - if selector != nil { - return selector - } - return nil - }(), - SolverID: solverID, - Subscribe: false, - }, - } -} - -// ForgePeeringCandidate creates a PeeringCandidate CR from a Flavour and a Discovery -func ForgePeeringCandidate(flavourPeeringCandidate *nodecorev1alpha1.Flavour, solverID string, reserved bool) (pc *advertisementv1alpha1.PeeringCandidate) { - pc = &advertisementv1alpha1.PeeringCandidate{ - ObjectMeta: metav1.ObjectMeta{ - Name: namings.ForgePeeringCandidateName(flavourPeeringCandidate.Name), - Namespace: flags.FLUIDOS_NAMESPACE, - }, - Spec: advertisementv1alpha1.PeeringCandidateSpec{ - Flavour: nodecorev1alpha1.Flavour{ - ObjectMeta: metav1.ObjectMeta{ - Name: flavourPeeringCandidate.Name, - Namespace: flavourPeeringCandidate.Namespace, - }, - Spec: flavourPeeringCandidate.Spec, - }, - }, - } - - if reserved { - pc.Spec.SolverID = solverID - pc.Spec.Reserved = true - } - - return -} - -// ForgeReservation creates a Reservation CR from a PeeringCandidate -func ForgeReservation(peeringCandidate advertisementv1alpha1.PeeringCandidate, partition *reservationv1alpha1.Partition, ni nodecorev1alpha1.NodeIdentity) *reservationv1alpha1.Reservation { - solverID := peeringCandidate.Spec.SolverID - reservation := &reservationv1alpha1.Reservation{ - ObjectMeta: metav1.ObjectMeta{ - Name: namings.ForgeReservationName(solverID), - Namespace: flags.FLUIDOS_NAMESPACE, - }, - Spec: reservationv1alpha1.ReservationSpec{ - SolverID: solverID, - Buyer: ni, - Seller: nodecorev1alpha1.NodeIdentity{ - Domain: peeringCandidate.Spec.Flavour.Spec.Owner.Domain, - NodeID: peeringCandidate.Spec.Flavour.Spec.Owner.NodeID, - IP: peeringCandidate.Spec.Flavour.Spec.Owner.IP, - }, - PeeringCandidate: nodecorev1alpha1.GenericRef{ - Name: peeringCandidate.Name, - Namespace: peeringCandidate.Namespace, - }, - Reserve: true, - Purchase: true, - }, - } - if partition != nil { - reservation.Spec.Partition = partition - } - return reservation -} - -// ForgeContract creates a Contract CR -func ForgeContract(flavour nodecorev1alpha1.Flavour, transaction models.Transaction, lc *reservationv1alpha1.LiqoCredentials) *reservationv1alpha1.Contract { - return &reservationv1alpha1.Contract{ - ObjectMeta: metav1.ObjectMeta{ - Name: namings.ForgeContractName(flavour.Name), - Namespace: flags.FLUIDOS_NAMESPACE, - }, - Spec: reservationv1alpha1.ContractSpec{ - Flavour: flavour, - Buyer: nodecorev1alpha1.NodeIdentity{ - Domain: transaction.Buyer.Domain, - IP: transaction.Buyer.IP, - NodeID: transaction.Buyer.NodeID, - }, - BuyerClusterID: transaction.ClusterID, - Seller: flavour.Spec.Owner, - SellerCredentials: *lc, - TransactionID: transaction.TransactionID, - Partition: func() *reservationv1alpha1.Partition { - if transaction.Partition != nil { - return parseutil.ParsePartitionFromObj(transaction.Partition) - } - return nil - }(), - ExpirationTime: time.Now().Add(flags.EXPIRATION_CONTRACT).Format(time.RFC3339), - }, - Status: reservationv1alpha1.ContractStatus{ - Phase: nodecorev1alpha1.PhaseStatus{ - Phase: nodecorev1alpha1.PhaseActive, - StartTime: tools.GetTimeNow(), - }, - }, - } -} - -// ForgeFlavourFromMetrics creates a new flavour custom resource from the metrics of the node -func ForgeFlavourFromMetrics(node models.NodeInfo, ni nodecorev1alpha1.NodeIdentity) (flavour *nodecorev1alpha1.Flavour) { - return &nodecorev1alpha1.Flavour{ - ObjectMeta: metav1.ObjectMeta{ - Name: namings.ForgeFlavourName(node.UID, ni.Domain), - Namespace: flags.FLUIDOS_NAMESPACE, - }, - Spec: nodecorev1alpha1.FlavourSpec{ - ProviderID: ni.NodeID, - Type: nodecorev1alpha1.K8S, - Characteristics: nodecorev1alpha1.Characteristics{ - Architecture: node.Architecture, - Cpu: node.ResourceMetrics.CPUAvailable, - Memory: node.ResourceMetrics.MemoryAvailable, - EphemeralStorage: node.ResourceMetrics.EphemeralStorage, - PersistentStorage: parseutil.ParseQuantityFromString("0"), - Gpu: parseutil.ParseQuantityFromString("0"), - }, - Policy: nodecorev1alpha1.Policy{ - Partitionable: &nodecorev1alpha1.Partitionable{ - CpuMin: parseutil.ParseQuantityFromString(flags.CPU_MIN), - MemoryMin: parseutil.ParseQuantityFromString(flags.MEMORY_MIN), - CpuStep: parseutil.ParseQuantityFromString(flags.CPU_STEP), - MemoryStep: parseutil.ParseQuantityFromString(flags.MEMORY_STEP), - }, - Aggregatable: &nodecorev1alpha1.Aggregatable{ - MinCount: int(flags.MIN_COUNT), - MaxCount: int(flags.MAX_COUNT), - }, - }, - Owner: ni, - Price: nodecorev1alpha1.Price{ - Amount: flags.AMOUNT, - Currency: flags.CURRENCY, - Period: flags.PERIOD, - }, - OptionalFields: nodecorev1alpha1.OptionalFields{ - Availability: true, - WorkerID: node.UID, - }, - }, - } -} - -// FORGER FUNCTIONS FROM OBJECTS - -// ForgeTransaction creates a new transaction -func ForgeTransactionObj(ID string, req models.ReserveRequest) models.Transaction { - return models.Transaction{ - TransactionID: ID, - Buyer: req.Buyer, - ClusterID: req.ClusterID, - FlavourID: req.FlavourID, - Partition: req.Partition, - StartTime: tools.GetTimeNow(), - } -} - -func ForgeContractObj(contract *reservationv1alpha1.Contract) models.Contract { - return models.Contract{ - ContractID: contract.Name, - Flavour: parseutil.ParseFlavour(contract.Spec.Flavour), - Buyer: parseutil.ParseNodeIdentity(contract.Spec.Buyer), - BuyerClusterID: contract.Spec.BuyerClusterID, - Seller: parseutil.ParseNodeIdentity(contract.Spec.Seller), - SellerCredentials: models.LiqoCredentials{ - ClusterID: contract.Spec.SellerCredentials.ClusterID, - ClusterName: contract.Spec.SellerCredentials.ClusterName, - Token: contract.Spec.SellerCredentials.Token, - Endpoint: contract.Spec.SellerCredentials.Endpoint, - }, - Partition: func() *models.Partition { - if contract.Spec.Partition != nil { - return parseutil.ParsePartition(contract.Spec.Partition) - } - return nil - }(), - TransactionID: contract.Spec.TransactionID, - ExpirationTime: contract.Spec.ExpirationTime, - ExtraInformation: func() map[string]string { - if contract.Spec.ExtraInformation != nil { - return contract.Spec.ExtraInformation - } - return nil - }(), - } -} - -// ForgeResponsePurchaseObj creates a new response purchase -func ForgeResponsePurchaseObj(contract models.Contract) models.ResponsePurchase { - return models.ResponsePurchase{ - Contract: contract, - Status: "Completed", - } -} - -// ForgeContractFromObj creates a Contract from a reservation -func ForgeContractFromObj(contract models.Contract) *reservationv1alpha1.Contract { - return &reservationv1alpha1.Contract{ - ObjectMeta: metav1.ObjectMeta{ - Name: contract.ContractID, - Namespace: flags.FLUIDOS_NAMESPACE, - }, - Spec: reservationv1alpha1.ContractSpec{ - Flavour: *ForgeFlavourFromObj(contract.Flavour), - Buyer: nodecorev1alpha1.NodeIdentity{ - Domain: contract.Buyer.Domain, - IP: contract.Buyer.IP, - NodeID: contract.Buyer.NodeID, - }, - BuyerClusterID: contract.BuyerClusterID, - Seller: nodecorev1alpha1.NodeIdentity{ - NodeID: contract.Seller.NodeID, - IP: contract.Seller.IP, - Domain: contract.Seller.Domain, - }, - SellerCredentials: reservationv1alpha1.LiqoCredentials{ - ClusterID: contract.SellerCredentials.ClusterID, - ClusterName: contract.SellerCredentials.ClusterName, - Token: contract.SellerCredentials.Token, - Endpoint: contract.SellerCredentials.Endpoint, - }, - TransactionID: contract.TransactionID, - Partition: func() *reservationv1alpha1.Partition { - if contract.Partition != nil { - return parseutil.ParsePartitionFromObj(contract.Partition) - } - return nil - }(), - ExpirationTime: contract.ExpirationTime, - ExtraInformation: func() map[string]string { - if contract.ExtraInformation != nil { - return contract.ExtraInformation - } - return nil - }(), - }, - Status: reservationv1alpha1.ContractStatus{ - Phase: nodecorev1alpha1.PhaseStatus{ - Phase: nodecorev1alpha1.PhaseActive, - StartTime: tools.GetTimeNow(), - }, - }, - } -} - -// ForgeTransactionFromObj creates a transaction from a Transaction object -func ForgeTransactionFromObj(reservation *models.Transaction) *reservationv1alpha1.Transaction { - return &reservationv1alpha1.Transaction{ - ObjectMeta: metav1.ObjectMeta{ - Name: reservation.TransactionID, - Namespace: flags.FLUIDOS_NAMESPACE, - }, - Spec: reservationv1alpha1.TransactionSpec{ - FlavourID: reservation.FlavourID, - StartTime: reservation.StartTime, - Buyer: nodecorev1alpha1.NodeIdentity{ - Domain: reservation.Buyer.Domain, - IP: reservation.Buyer.IP, - NodeID: reservation.Buyer.NodeID, - }, - ClusterID: reservation.ClusterID, - Partition: func() *reservationv1alpha1.Partition { - if reservation.Partition != nil { - return parseutil.ParsePartitionFromObj(reservation.Partition) - } - return nil - }(), - }, - } -} - -// ForgeFlavourFromObj creates a Flavour CR from a Flavour Object (REAR) -func ForgeFlavourFromObj(flavour models.Flavour) *nodecorev1alpha1.Flavour { - f := &nodecorev1alpha1.Flavour{ - ObjectMeta: metav1.ObjectMeta{ - Name: flavour.FlavourID, - Namespace: flags.FLUIDOS_NAMESPACE, - }, - Spec: nodecorev1alpha1.FlavourSpec{ - ProviderID: flavour.Owner.NodeID, - Type: nodecorev1alpha1.K8S, - Characteristics: nodecorev1alpha1.Characteristics{ - Cpu: flavour.Characteristics.CPU, - Memory: flavour.Characteristics.Memory, - Architecture: flavour.Characteristics.Architecture, - EphemeralStorage: flavour.Characteristics.EphemeralStorage, - PersistentStorage: flavour.Characteristics.PersistentStorage, - Gpu: flavour.Characteristics.Gpu, - }, - Policy: nodecorev1alpha1.Policy{ - // Check if flavour.Partitionable is not nil before setting Partitionable - Partitionable: func() *nodecorev1alpha1.Partitionable { - if flavour.Policy.Partitionable != nil { - return &nodecorev1alpha1.Partitionable{ - CpuMin: flavour.Policy.Partitionable.CPUMinimum, - MemoryMin: flavour.Policy.Partitionable.MemoryMinimum, - CpuStep: flavour.Policy.Partitionable.CPUStep, - MemoryStep: flavour.Policy.Partitionable.MemoryStep, - } - } - return nil - }(), - Aggregatable: func() *nodecorev1alpha1.Aggregatable { - if flavour.Policy.Aggregatable != nil { - return &nodecorev1alpha1.Aggregatable{ - MinCount: flavour.Policy.Aggregatable.MinCount, - MaxCount: flavour.Policy.Aggregatable.MaxCount, - } - } - return nil - }(), - }, - Owner: nodecorev1alpha1.NodeIdentity{ - Domain: flavour.Owner.Domain, - IP: flavour.Owner.IP, - NodeID: flavour.Owner.NodeID, - }, - Price: nodecorev1alpha1.Price{ - Amount: flavour.Price.Amount, - Currency: flavour.Price.Currency, - Period: flavour.Price.Period, - }, - }, - } - return f -} - -func ForgePartition(selector *nodecorev1alpha1.FlavourSelector) *reservationv1alpha1.Partition { - return &reservationv1alpha1.Partition{ - Architecture: selector.Architecture, - Cpu: selector.RangeSelector.MinCpu, - Memory: selector.RangeSelector.MinMemory, - EphemeralStorage: selector.RangeSelector.MinEph, - Storage: selector.RangeSelector.MinStorage, - Gpu: selector.RangeSelector.MinGpu, - } -} -skip file ./pkg/utils/resourceforge/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package namings - -import ( - "crypto/rand" - "crypto/sha256" - "encoding/hex" - "fmt" - "strings" - "time" - - "github.com/fluidos-project/node/pkg/utils/flags" - "k8s.io/klog/v2" -) - -// ForgeContractName creates a name for the Contract CR -func ForgeContractName(flavourID string) string { - hash := ForgeHashString(flavourID, 4) - return fmt.Sprintf("contract-%s-%s", flavourID, hash) -} - -// ForgePeeringCandidateName generates a name for the PeeringCandidate -func ForgePeeringCandidateName(flavourID string) string { - return fmt.Sprintf("peeringcandidate-%s", flavourID) -} - -// ForgeReservationName generates a name for the Reservation -func ForgeReservationName(solverID string) string { - return fmt.Sprintf("reservation-%s", solverID) -} - -// ForgeFlavourName returns the name of the flavour following the pattern Domain-Type-rand(4) -func ForgeFlavourName(WorkerID, domain string) string { - rand, err := ForgeRandomString() - if err != nil { - klog.Errorf("Error when generating random string: %s", err) - } - - return domain + "-" + flags.RESOURCE_TYPE + "-" + ForgeHashString(WorkerID+rand, 8) -} - -// ForgeDiscoveryName returns the name of the discovery following the pattern solverID-discovery -func ForgeDiscoveryName(solverID string) string { - return fmt.Sprintf("discovery-%s", solverID) -} - -func RetrieveSolverNameFromDiscovery(discoveryName string) string { - return strings.TrimPrefix(discoveryName, "discovery-") -} - -func RetrieveSolverNameFromReservation(reservationName string) string { - return strings.TrimPrefix(reservationName, "reservation-") -} - -// ForgeTransactionID Generates a unique transaction ID using the current timestamp -func ForgeTransactionID() (string, error) { - // Convert the random bytes to a hexadecimal string - transactionID, err := ForgeRandomString() - if err != nil { - return "", err - } - - // Add a timestamp to ensure uniqueness - transactionID = fmt.Sprintf("%s-%d", transactionID, time.Now().UnixNano()) - - return transactionID, nil -} - -// RetrieveFlavourNameFromPC generates a name for the Flavour from the PeeringCandidate -func RetrieveFlavourNameFromPC(pcName string) string { - return strings.TrimPrefix(pcName, "peeringcandidate-") -} - -// ForgePrefixClientID generates a prefix for the client ID -func ForgeRandomString() (string, error) { - randomBytes := make([]byte, 16) - _, err := rand.Read(randomBytes) - if err != nil { - return "", err - } - randomString := hex.EncodeToString(randomBytes) - - return randomString, nil -} - -// ForgeHashString computes SHA-256 Hash of the NodeUID -func ForgeHashString(input string, lenght int) string { - hash := sha256.Sum256([]byte(input)) - hashString := hex.EncodeToString(hash[:]) - uniqueString := hashString[:lenght] - - return uniqueString -} -skip file ./pkg/utils/namings/doc.go since no import -skip file ./pkg/local-resource-manager/doc.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package localResourceManager - -import ( - "context" - - "github.com/fluidos-project/node/pkg/utils/flags" - "github.com/fluidos-project/node/pkg/utils/models" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/klog/v2" - metricsv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// GetNodesResources retrieves the metrics from all the worker nodes in the cluster -func GetNodesResources(ctx context.Context, cl client.Client) (*[]models.NodeInfo, error) { - // Set a label selector to filter worker nodes - labelSelector := labels.Set{flags.RESOURCE_NODE_LABEL: "true"}.AsSelector() - - // Get a list of nodes - nodes := &corev1.NodeList{} - err := cl.List(ctx, nodes, &client.ListOptions{ - LabelSelector: labelSelector, - }) - if err != nil { - klog.Errorf("Error when listing nodes: %s", err) - return nil, err - } - - // Get a list of nodes metrics - nodesMetrics := &metricsv1beta1.NodeMetricsList{} - err = cl.List(ctx, nodesMetrics, &client.ListOptions{ - LabelSelector: labelSelector, - }) - if err != nil { - klog.Errorf("Error when listing nodes metrics: %s", err) - return nil, err - } - - var nodesInfo []models.NodeInfo - // Print the name of each node - for _, node := range nodes.Items { - for _, metrics := range nodesMetrics.Items { - if node.Name != metrics.Name { - // So that we can select just the nodes that we want - continue - } - metricsStruct := forgeResourceMetrics(&metrics, &node) - nodeInfo := forgeNodeInfo(&node, metricsStruct) - nodesInfo = append(nodesInfo, *nodeInfo) - } - } - - return &nodesInfo, nil -} - -// forgeResourceMetrics creates from params a new ResourceMetrics Struct -func forgeResourceMetrics(nodeMetrics *metricsv1beta1.NodeMetrics, node *corev1.Node) *models.ResourceMetrics { - // Get the total and used resources - cpuTotal := node.Status.Allocatable.Cpu() - cpuUsed := nodeMetrics.Usage.Cpu() - memoryTotal := node.Status.Allocatable.Memory() - memoryUsed := nodeMetrics.Usage.Memory() - ephemeralStorage := nodeMetrics.Usage.StorageEphemeral() - - // Compute the available resources - cpuAvail := cpuTotal.DeepCopy() - memAvail := memoryTotal.DeepCopy() - cpuAvail.Sub(*cpuUsed) - memAvail.Sub(*memoryUsed) - - return &models.ResourceMetrics{ - CPUTotal: *cpuTotal, - CPUAvailable: cpuAvail, - MemoryTotal: *memoryTotal, - MemoryAvailable: memAvail, - EphemeralStorage: *ephemeralStorage, - } -} - -// forgeNodeInfo creates from params a new NodeInfo Struct -func forgeNodeInfo(node *corev1.Node, metrics *models.ResourceMetrics) *models.NodeInfo { - return &models.NodeInfo{ - UID: string(node.UID), - Name: node.Name, - Architecture: node.Status.NodeInfo.Architecture, - OperatingSystem: node.Status.NodeInfo.OperatingSystem, - ResourceMetrics: *metrics, - } -} - -// maxNode find the node with the maximum value based on the provided comparison function -/* func maxNode(nodes []models.NodeInfo, compareFunc func(models.NodeInfo, models.NodeInfo) bool) models.NodeInfo { - if len(nodes) == 0 { - klog.Errorf("Error when finding the node with the maximum value: the list of nodes is empty") - return models.NodeInfo{} - } - - maxNode := nodes[0] - for _, node := range nodes { - if compareFunc(node, maxNode) { - maxNode = node - } - } - return maxNode -} */ -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package localResourceManager - -import ( - "context" - "fmt" - "log" - - "github.com/fluidos-project/node/pkg/utils/getters" - "github.com/fluidos-project/node/pkg/utils/resourceforge" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// clusterRole -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=flavours,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=core,resources=nodes,verbs=get;list;watch -//+kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch -//+kubebuilder:rbac:groups=metrics.k8s.io,resources=pods,verbs=get;list;watch -//+kubebuilder:rbac:groups=metrics.k8s.io,resources=nodes,verbs=get;list;watch - -// TODO: If the local resource manager restarts, -// ensure to check and subtract the already allocated resources from the node -// resources calculation. - -// Start starts the controller -func Start(ctx context.Context, cl client.Client) error { - - klog.Info("Getting FLUIDOS Node identity...") - - nodeIdentity := getters.GetNodeIdentity(ctx, cl) - if nodeIdentity == nil { - klog.Info("Error getting FLUIDOS Node identity") - return fmt.Errorf("Error getting FLUIDOS Node identity") - } - - klog.Info("Getting nodes resources...") - nodes, err := GetNodesResources(ctx, cl) - if err != nil { - log.Printf("Error getting nodes resources: %v", err) - return err - } - - klog.Infof("Creating Flavours: found %d nodes", len(*nodes)) - - // For each node create a Flavour - for _, node := range *nodes { - flavour := resourceforge.ForgeFlavourFromMetrics(node, *nodeIdentity) - err := cl.Create(ctx, flavour) - if err != nil { - log.Printf("Error creating Flavour: %v", err) - return err - } - klog.Infof("Flavour created: %s", flavour.Name) - } - - return nil -} -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package rearmanager - -import ( - "context" - - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - - //admissionv1 "k8s.io/api/admission/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -type Validator struct { - client client.Client - decoder *admission.Decoder -} - -func NewValidator(client client.Client) *Validator { - return &Validator{client: client, decoder: admission.NewDecoder(runtime.NewScheme())} -} - -func (v *Validator) Handle(ctx context.Context, req admission.Request) admission.Response { - - return admission.Allowed("allowed") -} - -func (v *Validator) HandleCreate(ctx context.Context, req admission.Request) admission.Response { - return admission.Allowed("allowed") -} - -func (v *Validator) HandleDelete(ctx context.Context, req admission.Request) admission.Response { - return admission.Allowed("allowed") -} - -func (v *Validator) HandleUpdate(ctx context.Context, req admission.Request) admission.Response { - return admission.Allowed("allowed") -} - -/* func (v *Validator) InjectDecoder(d *admission.Decoder) error { - v.decoder = d - return nil -} */ - -func (v *Validator) DecodeAllocation(obj runtime.RawExtension) (pc *nodecorev1alpha1.Allocation, err error) { - pc = &nodecorev1alpha1.Allocation{} - err = v.decoder.DecodeRaw(obj, pc) - return -} -/* -Copyright 2023. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rearmanager - -import ( - "context" - - advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - reservationv1alpha1 "github.com/fluidos-project/node/apis/reservation/v1alpha1" - "github.com/fluidos-project/node/pkg/utils/common" - "github.com/fluidos-project/node/pkg/utils/flags" - "github.com/fluidos-project/node/pkg/utils/getters" - "github.com/fluidos-project/node/pkg/utils/namings" - "github.com/fluidos-project/node/pkg/utils/resourceforge" - "github.com/fluidos-project/node/pkg/utils/tools" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -// SolverReconciler reconciles a Solver object -type SolverReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -// clusterRole -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=solvers,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=solvers/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=solvers/finalizers,verbs=update -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=flavours,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=flavours/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=flavours/finalizers,verbs=update -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=peeringcandidates,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=peeringcandidates/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=peeringcandidates/finalizers,verbs=update -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=discoveries,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=discoveries/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=advertisement.fluidos.eu,resources=discoveries/finalizers,verbs=update -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=reservations,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=reservations/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=reservations/finalizers,verbs=update -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=contracts,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=contracts/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=reservation.fluidos.eu,resources=contracts/finalizers,verbs=update -//+kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch - -func (r *SolverReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := ctrl.LoggerFrom(ctx, "solver", req.NamespacedName) - ctx = ctrl.LoggerInto(ctx, log) - //_ = log.FromContext(ctx) - - var solver nodecorev1alpha1.Solver - if err := r.Get(ctx, req.NamespacedName, &solver); client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when getting Solver %s before reconcile: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } else if err != nil { - klog.Infof("Solver %s not found, probably deleted", req.NamespacedName) - return ctrl.Result{}, nil - } - - if solver.Status.SolverPhase.Phase != nodecorev1alpha1.PhaseSolved && - solver.Status.SolverPhase.Phase != nodecorev1alpha1.PhaseTimeout && - solver.Status.SolverPhase.Phase != nodecorev1alpha1.PhaseFailed && - solver.Status.SolverPhase.Phase != nodecorev1alpha1.PhaseRunning && - solver.Status.SolverPhase.Phase != nodecorev1alpha1.PhaseIdle { - - solver.SetPhase(nodecorev1alpha1.PhaseIdle, "Solver initialized") - - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status before reconcile: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - klog.Infof("Reconciling Solver %s", req.NamespacedName) - - findCandidateStatus := solver.Status.FindCandidate - reserveAndBuyStatus := solver.Status.ReserveAndBuy - - // Check if the Solver has expired or failed, in this case do nothing and return - if solver.Status.SolverPhase.Phase == nodecorev1alpha1.PhaseFailed || - solver.Status.SolverPhase.Phase == nodecorev1alpha1.PhaseTimeout || - solver.Status.SolverPhase.Phase == nodecorev1alpha1.PhaseSolved { - return ctrl.Result{}, nil - } - - if solver.Spec.FindCandidate { - switch findCandidateStatus { - case nodecorev1alpha1.PhaseIdle: - // Search a matching PeeringCandidate if available - pc, err := r.searchPeeringCandidates(ctx, &solver) - if client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when searching and booking a candidate for Solver %s: %s", req.NamespacedName.Name, err) - return ctrl.Result{}, err - } - - // If some PeeringCandidates are available, select one and book it - if len(pc) > 0 { - // If some PeeringCandidates are available, select one and book it - selectedPc, err := r.selectAndBookPeeringCandidate(ctx, &solver, pc) - if err != nil { - klog.Errorf("Error when selecting and booking a candidate for Solver %s: %s", req.NamespacedName.Name, err) - return ctrl.Result{}, err - } - klog.Infof("Solver %s has selected and booked candidate %s", req.NamespacedName.Name, selectedPc.Name) - solver.SetFindCandidateStatus(nodecorev1alpha1.PhaseSolved) - solver.SetPhase(nodecorev1alpha1.PhaseRunning, "Solver has found a candidate") - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - // If no PeeringCandidate is available, Create a Discovery - klog.Infof("Solver %s has not found any candidate. Trying a Discovery", req.NamespacedName.Name) - solver.SetFindCandidateStatus(nodecorev1alpha1.PhaseRunning) - solver.SetPhase(nodecorev1alpha1.PhaseRunning, "Solver is trying a Discovery") - - // Update the Solver status - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - case nodecorev1alpha1.PhaseRunning: - // Check solver expiration - if tools.CheckExpiration(solver.Status.SolverPhase.LastChangeTime, flags.EXPIRATION_PHASE_RUNNING) { - klog.Infof("Solver %s has expired", req.NamespacedName.Name) - - solver.SetPhase(nodecorev1alpha1.PhaseTimeout, "Solver has expired before finding a candidate") - - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - klog.Infof("Getting or creating Discovery for Solver %s", req.NamespacedName.Name) - discovery, err := r.createOrGetDiscovery(ctx, &solver) - if err != nil { - klog.Errorf("Error when creating or getting Discovery for Solver %s: %s", req.NamespacedName.Name, err) - return ctrl.Result{}, err - } - - common.DiscoveryStatusCheck(&solver, discovery) - - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - case nodecorev1alpha1.PhaseFailed: - klog.Infof("Solver %s has not found any candidate", req.NamespacedName.Name) - solver.SetPhase(nodecorev1alpha1.PhaseFailed, "Solver has not found any candidate") - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - case nodecorev1alpha1.PhaseSolved: - klog.Infof("Solver %s has found a candidate", req.NamespacedName.Name) - default: - solver.SetFindCandidateStatus(nodecorev1alpha1.PhaseIdle) - solver.SetPhase(nodecorev1alpha1.PhaseRunning, "Solver is running") - // Update the Solver status - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - } else { - klog.Infof("Solver %s Solved : No need to find a candidate", req.NamespacedName.Name) - solver.SetPhase(nodecorev1alpha1.PhaseSolved, "No need to find a candidate") - err := r.updateSolverStatus(ctx, &solver) - if err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - if solver.Spec.ReserveAndBuy { - if findCandidateStatus == nodecorev1alpha1.PhaseSolved { - klog.Infof("ReserveAndBuy %s", reserveAndBuyStatus) - switch reserveAndBuyStatus { - case nodecorev1alpha1.PhaseIdle: - var partition *reservationv1alpha1.Partition - klog.Infof("Creating the Reservation %s", req.NamespacedName.Name) - // Create the Reservation - var pc advertisementv1alpha1.PeeringCandidate - pcNamespaceName := types.NamespacedName{Name: solver.Status.PeeringCandidate.Name, Namespace: solver.Status.PeeringCandidate.Namespace} - - // Get the PeeringCandidate from the Solver - if err := r.Get(ctx, pcNamespaceName, &pc); err != nil { - klog.Errorf("Error when getting PeeringCandidate %s: %s", solver.Status.PeeringCandidate.Name, err) - return ctrl.Result{}, err - } - - if solver.Spec.Selector == nil { - // Forge the Partition - partition = resourceforge.ForgePartition(solver.Spec.Selector) - } - - // Get the NodeIdentity - nodeIdentity := getters.GetNodeIdentity(ctx, r.Client) - - // Forge the Reservation - reservation := resourceforge.ForgeReservation(pc, partition, *nodeIdentity) - if err := r.Client.Create(ctx, reservation); err != nil { - klog.Errorf("Error when creating Reservation for Solver %s: %s", solver.Name, err) - return ctrl.Result{}, err - } - - klog.Infof("Reservation %s created", reservation.Name) - - solver.SetReserveAndBuyStatus(nodecorev1alpha1.PhaseRunning) - solver.SetPhase(nodecorev1alpha1.PhaseRunning, "Reservation created") - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - case nodecorev1alpha1.PhaseRunning: - // Check solver expiration - if tools.CheckExpiration(solver.Status.SolverPhase.LastChangeTime, flags.EXPIRATION_PHASE_RUNNING) { - klog.Infof("Solver %s has expired", req.NamespacedName.Name) - solver.SetPhase(nodecorev1alpha1.PhaseTimeout, "Solver has expired before reserving the resources") - - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - reservation := &reservationv1alpha1.Reservation{} - resNamespaceName := types.NamespacedName{Name: namings.ForgeReservationName(solver.Name), Namespace: flags.FLUIDOS_NAMESPACE} - - // Get the Reservation - err := r.Get(ctx, resNamespaceName, reservation) - if client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when getting Reservation for Solver %s: %s", solver.Name, err) - return ctrl.Result{}, err - } - - common.ReservationStatusCheck(&solver, reservation) - - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - - case nodecorev1alpha1.PhaseFailed: - klog.Infof("Solver %s has failed to reserve and buy the resources", req.NamespacedName.Name) - solver.SetPhase(nodecorev1alpha1.PhaseFailed, "Solver has failed to reserve the resources") - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - case nodecorev1alpha1.PhaseSolved: - klog.Infof("Solver %s has reserved and purchased the resources", req.NamespacedName.Name) - default: - solver.SetReserveAndBuyStatus(nodecorev1alpha1.PhaseIdle) - // Update the Solver status - if err := r.updateSolverStatus(ctx, &solver); err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - } - } else { - klog.Infof("Solver %s Solved : No need to reserve and buy the resources", req.NamespacedName.Name) - solver.SetPhase(nodecorev1alpha1.PhaseSolved, "No need to reserve and buy the resources") - err := r.updateSolverStatus(ctx, &solver) - if err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - if solver.Spec.EnstablishPeering { - if reserveAndBuyStatus == nodecorev1alpha1.PhaseSolved { - // Peering phase to be implemented - klog.Infof("Solver %s Solved : Peering phase to be implemented", req.NamespacedName.Name) - } - } else { - klog.Infof("Solver %s Solved : No need to enstablish a peering", req.NamespacedName.Name) - solver.SetPhase(nodecorev1alpha1.PhaseSolved, "No need to enstablish a peering") - err := r.updateSolverStatus(ctx, &solver) - if err != nil { - klog.Errorf("Error when updating Solver %s status: %s", req.NamespacedName, err) - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - - return ctrl.Result{}, nil -} - -func (r *SolverReconciler) searchPeeringCandidates(ctx context.Context, solver *nodecorev1alpha1.Solver) ([]advertisementv1alpha1.PeeringCandidate, error) { - pc := advertisementv1alpha1.PeeringCandidateList{} - result := []advertisementv1alpha1.PeeringCandidate{} - - // Get the Flavour Selector from the Solver - selector := solver.Spec.Selector - - // Get the list of PeeringCandidates - if err := r.List(ctx, &pc); err != nil { - klog.Errorf("Error when listing PeeringCandidates: %s", err) - return nil, err - } - - // TODO: Maybe not needed - if len(pc.Items) == 0 { - klog.Infof("No PeeringCandidates found") - return nil, errors.NewNotFound(schema.GroupResource{Group: "advertisement", Resource: "PeeringCandidate"}, "PeeringCandidate") - } - - // Filter the reserved PeeringCandidates - filtered := []advertisementv1alpha1.PeeringCandidate{} - for _, p := range pc.Items { - if !p.Spec.Reserved && p.Spec.SolverID == "" { - filtered = append(filtered, p) - } - } - - // Filter the list of PeeringCandidates based on the Flavour Selector - for _, p := range filtered { - res := common.FilterPeeringCandidate(selector, &p) - if res { - result = append(result, p) - } - } - - return result, nil -} - -// TODO: unify this logic with the one of the discovery controller -func (r *SolverReconciler) selectAndBookPeeringCandidate(ctx context.Context, solver *nodecorev1alpha1.Solver, pcList []advertisementv1alpha1.PeeringCandidate) (*advertisementv1alpha1.PeeringCandidate, error) { - // Select the first PeeringCandidate - - var selected *advertisementv1alpha1.PeeringCandidate - - for _, pc := range pcList { - // Select the first PeeringCandidate that is not reserved - if !pc.Spec.Reserved && pc.Spec.SolverID == "" { - // Book the PeeringCandidate - pc.Spec.Reserved = true - pc.Spec.SolverID = solver.Name - - // Update the PeeringCandidate - if err := r.Update(ctx, &pc); err != nil { - klog.Errorf("Error when updating PeeringCandidate %s: %s", selected.Name, err) - continue - } - - // Getting the just updated PeeringCandidate - if err := r.Get(ctx, types.NamespacedName{Name: pc.Name, Namespace: pc.Namespace}, selected); err != nil { - klog.Errorf("Error when getting the reserved PeeringCandidate %s: %s", selected.Name, err) - continue - } - - // Check if the PeeringCandidate has been reserved correctly - if !pc.Spec.Reserved || pc.Spec.SolverID != solver.Name { - klog.Errorf("Error when reserving PeeringCandidate %s. Trying with another one", selected.Name) - continue - } - - break - } - } - - // check if a PeeringCandidate has been selected - if selected == nil || selected.Name == "" { - klog.Infof("No PeeringCandidate selected") - return nil, errors.NewNotFound(schema.GroupResource{Group: "advertisement", Resource: "PeeringCandidate"}, "PeeringCandidate") - } - - return selected, nil -} - -func (r *SolverReconciler) createOrGetDiscovery(ctx context.Context, solver *nodecorev1alpha1.Solver) (*advertisementv1alpha1.Discovery, error) { - discovery := &advertisementv1alpha1.Discovery{} - - // Get the Discovery - if err := r.Get(ctx, types.NamespacedName{Name: namings.ForgeDiscoveryName(solver.Name), Namespace: flags.FLUIDOS_NAMESPACE}, discovery); client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when getting Discovery for Solver %s: %s", solver.Name, err) - return nil, err - } else if err != nil { - // Create the Discovery - discovery := resourceforge.ForgeDiscovery(solver.Spec.Selector, solver.Name) - if err := r.Client.Create(ctx, discovery); err != nil { - klog.Errorf("Error when creating Discovery for Solver %s: %s", solver.Name, err) - return nil, err - } - } - return discovery, nil -} - -func (r *SolverReconciler) updateSolverStatus(ctx context.Context, solver *nodecorev1alpha1.Solver) error { - return r.Status().Update(ctx, solver) -} - -// SetupWithManager sets up the controller with the Manager. -func (r *SolverReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&nodecorev1alpha1.Solver{}). - Watches(&advertisementv1alpha1.Discovery{}, handler.EnqueueRequestsFromMapFunc( - r.discoveryToSolver, - ), builder.WithPredicates(discoveryPredicate())). - Watches(&reservationv1alpha1.Reservation{}, handler.EnqueueRequestsFromMapFunc( - r.reservationToSolver, - ), builder.WithPredicates(reservationPredicate())). - Complete(r) -} - -func discoveryPredicate() predicate.Predicate { - return predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - return e.ObjectNew.(*advertisementv1alpha1.Discovery).Status.Phase.Phase == nodecorev1alpha1.PhaseSolved || - e.ObjectNew.(*advertisementv1alpha1.Discovery).Status.Phase.Phase == nodecorev1alpha1.PhaseFailed - }, - } -} - -func reservationPredicate() predicate.Predicate { - return predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - return e.ObjectNew.(*reservationv1alpha1.Reservation).Status.Phase.Phase == nodecorev1alpha1.PhaseSolved || - e.ObjectNew.(*reservationv1alpha1.Reservation).Status.Phase.Phase == nodecorev1alpha1.PhaseFailed - }, - } -} - -func (r *SolverReconciler) discoveryToSolver(ctx context.Context, o client.Object) []reconcile.Request { - solverName := namings.RetrieveSolverNameFromDiscovery(o.GetName()) - return []reconcile.Request{ - { - NamespacedName: types.NamespacedName{ - Name: solverName, - Namespace: flags.FLUIDOS_NAMESPACE, - }, - }, - } -} - -func (r *SolverReconciler) reservationToSolver(ctx context.Context, o client.Object) []reconcile.Request { - solverName := namings.RetrieveSolverNameFromReservation(o.GetName()) - return []reconcile.Request{ - { - NamespacedName: types.NamespacedName{ - Name: solverName, - Namespace: flags.FLUIDOS_NAMESPACE, - }, - }, - } -} -skip file ./pkg/rear-manager/doc.go since no import -skip file ./pkg/rear-manager/utils.go since no import -// Copyright 2022-2023 FLUIDOS Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package rearmanager - -import ( - "context" - - nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// +kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=allocations,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=allocations/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=allocations/finalizers,verbs=update - -// AllocationReconciler reconciles a Allocation object -type AllocationReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -func (r *AllocationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := ctrl.LoggerFrom(ctx, "allocation", req.NamespacedName) - ctx = ctrl.LoggerInto(ctx, log) - - var allocation nodecorev1alpha1.Allocation - if err := r.Get(ctx, req.NamespacedName, &allocation); client.IgnoreNotFound(err) != nil { - klog.Errorf("Error when getting Allocation %s before reconcile: %v", req.NamespacedName, err) - return ctrl.Result{}, err - } else if err != nil { - klog.Infof("Allocation %s not found, probably deleted", req.NamespacedName) - return ctrl.Result{}, nil - } - - return ctrl.Result{}, nil -} - -func (r *AllocationReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&nodecorev1alpha1.Allocation{}). - Complete(r) -} -find . -type f -name '*.go' -exec /Users/alessandro/go/bin/addlicense -l apache -c "FLUIDOS Project" -y "2022-2023" {} \;