diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7dbf1c7..5460079 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -2,7 +2,7 @@ name: Build Pipeline on: push: tags: - - 'v*' + - "v*" branches: - main @@ -23,52 +23,52 @@ jobs: architectures: ${{ steps.configure.outputs.architectures }} pr-number: ${{ steps.configure.outputs.pr-number }} steps: - - name: Get the version - id: get_version - run: echo "VERSION=$(echo $GITHUB_REF | cut -d / -f 3)" >> $GITHUB_OUTPUT - if: startsWith(github.ref, 'refs/tags/v') - - name: Configure - id: configure - run: | - # The ref of the commit to checkout (do not use the merge commit if repository dispatch) - if [ "${{ github.event_name }}" == "repository_dispatch" ]; then - echo "master=false" >> $GITHUB_OUTPUT - echo "architectures=linux/amd64" >> $GITHUB_OUTPUT - echo "commit-ref=${{ github.event.client_payload.pull_request.head.sha }}" >> $GITHUB_OUTPUT - echo "pr-number=${{ github.event.client_payload.github.payload.issue.number }}" >> $GITHUB_OUTPUT - elif [ "${{ steps.get_version.outputs.VERSION }}" != "" ]; then - echo "master=false" >> $GITHUB_OUTPUT - echo "architectures=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT - echo "commit-ref=${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_OUTPUT - else - echo "master=true" >> $GITHUB_OUTPUT - echo "architectures=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT - echo "commit-ref=${{ github.sha }}" >> $GITHUB_OUTPUT - fi - - # The suffix to append to the repository name if not triggered by a push for a release - ([[ "${{ steps.get_version.outputs.VERSION }}" == "" ]] && \ - echo "repo-suffix=-ci" || - echo "repo-suffix=") >> $GITHUB_OUTPUT - - if [ "${{ github.event_name }}" != "repository_dispatch" ]; then - echo "repo-name=${{ github.repository }}" >> $GITHUB_OUTPUT - else - echo "repo-name=${{ github.event.client_payload.github.payload.repository.full_name }}" >> $GITHUB_OUTPUT - fi - - # Since we are using a repository-dispatch event, we have to explicitly set a run check. We initialize it to a "pending" state. -# - uses: octokit/request-action@v2.x -# name: "Initialize run check to 'pending (For PR-only)" -# with: -# route: POST /repos/${{ github.repository }}/statuses/${{ steps.configure.outputs.commit-ref }} -# state: "pending" -# description: "Component build status" -# context: "Components building" -# target_url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# if: ${{ github.event_name == 'repository_dispatch' }} + - name: Get the version + id: get_version + run: echo "VERSION=$(echo $GITHUB_REF | cut -d / -f 3)" >> $GITHUB_OUTPUT + if: startsWith(github.ref, 'refs/tags/v') + - name: Configure + id: configure + run: | + # The ref of the commit to checkout (do not use the merge commit if repository dispatch) + if [ "${{ github.event_name }}" == "repository_dispatch" ]; then + echo "master=false" >> $GITHUB_OUTPUT + echo "architectures=linux/amd64" >> $GITHUB_OUTPUT + echo "commit-ref=${{ github.event.client_payload.pull_request.head.sha }}" >> $GITHUB_OUTPUT + echo "pr-number=${{ github.event.client_payload.github.payload.issue.number }}" >> $GITHUB_OUTPUT + elif [ "${{ steps.get_version.outputs.VERSION }}" != "" ]; then + echo "master=false" >> $GITHUB_OUTPUT + echo "architectures=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT + echo "commit-ref=${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_OUTPUT + else + echo "master=true" >> $GITHUB_OUTPUT + echo "architectures=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT + echo "commit-ref=${{ github.sha }}" >> $GITHUB_OUTPUT + fi + + # The suffix to append to the repository name if not triggered by a push for a release + ([[ "${{ steps.get_version.outputs.VERSION }}" == "" ]] && \ + echo "repo-suffix=-ci" || + echo "repo-suffix=") >> $GITHUB_OUTPUT + + if [ "${{ github.event_name }}" != "repository_dispatch" ]; then + echo "repo-name=${{ github.repository }}" >> $GITHUB_OUTPUT + else + echo "repo-name=${{ github.event.client_payload.github.payload.repository.full_name }}" >> $GITHUB_OUTPUT + fi + + # Since we are using a repository-dispatch event, we have to explicitly set a run check. We initialize it to a "pending" state. + # - uses: octokit/request-action@v2.x + # name: "Initialize run check to 'pending (For PR-only)" + # with: + # route: POST /repos/${{ github.repository }}/statuses/${{ steps.configure.outputs.commit-ref }} + # state: "pending" + # description: "Component build status" + # context: "Components building" + # target_url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # if: ${{ github.event_name == 'repository_dispatch' }} build: permissions: @@ -81,9 +81,9 @@ jobs: fail-fast: false matrix: component: - - rear-manager - - rear-controller - - local-resource-manager + - rear-manager + - rear-controller + - local-resource-manager steps: - name: Set up QEMU uses: docker/setup-qemu-action@v3.0.0 @@ -140,4 +140,94 @@ jobs: - name: Move cache run: | rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache \ No newline at end of file + mv /tmp/.buildx-cache-new /tmp/.buildx-cache + + post-build: + runs-on: ubuntu-latest + needs: [build, configure] + steps: + # Since we are using a repository-dispatch event, we have to explicitly set a run check. We update it to the actual status. + - uses: octokit/request-action@v2.x + name: "Set the PR check status (For PR-only)" + with: + route: POST /repos/${{ github.repository }}/statuses/${{ needs.configure.outputs.commit-ref }} + state: "${{ needs.build.result }}" + description: "Component build status" + context: "Components building" + target_url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + env: + GITHUB_TOKEN: ${{ secrets.CI_TOKEN }} + if: ${{ !cancelled() && github.event_name == 'repository_dispatch' }} + + release: + runs-on: ubuntu-latest + needs: [build, configure] + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # The changelog generation requires the entire history + fetch-depth: 0 + ref: "${{ needs.configure.outputs.commit-ref }}" + repository: "${{ needs.configure.outputs.repo-name }}" + persist-credentials: false + + - name: Get the latest Fluidos release + uses: pozetroninc/github-action-get-latest-release@v0.7.0 + id: last-release + with: + repository: ${{ github.repository }} + excludes: prerelease, draft + + - name: Configure Git + run: | + git config user.name "adamjensenbot" + git config user.email "adamjensenbot@users.noreply.github.com" + + - name: Install Helm + uses: azure/setup-helm@v3 + with: + version: v3.8.1 + + - name: Parse semver string + id: semver_parser + uses: booxmedialtd/ws-action-parse-semver@v1 + with: + input_string: "${{ needs.configure.outputs.commit-ref }}" + version_extractor_regex: "v(.*)$" + + - name: Download chart releaser + run: | + curl -sSL "https://github.com/helm/chart-releaser/releases/download/v1.4.0/chart-releaser_1.4.0_linux_amd64.tar.gz" | tar -xz + + - name: Package helm chart + run: | + # the output should be in the .cr-release-packages since cr index expects to find it there to create the helm index + helm package deployments/node --version "${{ needs.configure.outputs.commit-ref }}" --app-version "${{ needs.configure.outputs.commit-ref }}" --destination .cr-release-packages + + # we need to upload only helm artifacts first, to make the index updater to work + - uses: ncipollo/release-action@v1 + with: + artifacts: ".cr-release-packages/*" + generateReleaseNotes: true + token: ${{ secrets.CI_TOKEN }} + allowUpdates: true + tag: ${{ needs.configure.outputs.commit-ref }} + name: ${{ needs.configure.outputs.commit-ref }} + prerelease: ${{ steps.semver_parser.outputs.prerelease != '' }} + + - name: Update Helm index + run: | + repo=$(cut -d '/' -f 2 <<< "$GITHUB_REPOSITORY") + owner=$(cut -d '/' -f 1 <<< "$GITHUB_REPOSITORY") + + # Update index and push to github pages + ./cr index \ + --owner "$owner" \ + --git-repo "$repo" \ + --release-name-template "${{ needs.configure.outputs.commit-ref }}" \ + --token "${{ secrets.GITHUB_TOKEN }}" \ + --index-path index.yaml \ + --pages-index-path index.yaml \ + --push diff --git a/.gitignore b/.gitignore index 5a698c7..9caa221 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ config *.swp *.swo *~ -*.tgz +#*.tgz .vscode .devcontainer .DS_Store diff --git a/Makefile b/Makefile index 88d8cca..2189fd2 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ rbacs: controller-gen # Install gci if not available gci: ifeq (, $(shell which gci)) - @go install github.com/daixiang0/gci@v0.11.0 + @go install github.com/daixiang0/gci@v0.11.2 GCI=$(GOBIN)/gci else GCI=$(shell which gci) @@ -59,7 +59,7 @@ fmt: gci addlicense # Install golangci-lint if not available golangci-lint: ifeq (, $(shell which golangci-lint)) - @go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.3 + @go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.2 GOLANGCILINT=$(GOBIN)/golangci-lint else GOLANGCILINT=$(shell which golangci-lint) diff --git a/apis/advertisement/v1alpha1/discovery_types.go b/apis/advertisement/v1alpha1/discovery_types.go index d92b888..744a322 100644 --- a/apis/advertisement/v1alpha1/discovery_types.go +++ b/apis/advertisement/v1alpha1/discovery_types.go @@ -32,7 +32,7 @@ type DiscoverySpec struct { // 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"` + 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 diff --git a/apis/advertisement/v1alpha1/zz_generated.deepcopy.go b/apis/advertisement/v1alpha1/zz_generated.deepcopy.go index f479f32..9d747cb 100644 --- a/apis/advertisement/v1alpha1/zz_generated.deepcopy.go +++ b/apis/advertisement/v1alpha1/zz_generated.deepcopy.go @@ -19,6 +19,7 @@ package v1alpha1 import ( + nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -84,7 +85,11 @@ func (in *DiscoveryList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DiscoverySpec) DeepCopyInto(out *DiscoverySpec) { *out = *in - in.Selector.DeepCopyInto(&out.Selector) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(nodecorev1alpha1.FlavourSelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiscoverySpec. diff --git a/apis/nodecore/v1alpha1/common.go b/apis/nodecore/v1alpha1/common.go index f340480..4dfab3f 100644 --- a/apis/nodecore/v1alpha1/common.go +++ b/apis/nodecore/v1alpha1/common.go @@ -45,9 +45,9 @@ type NodeIdentity struct { } // toString() returns a string representation of the GenericRef. -func (r GenericRef) toString() string { +/* func (r GenericRef) toString() string { if r.Namespace != "" { return r.Namespace + "/" + r.Name } return r.Name -} +} */ diff --git a/apis/nodecore/v1alpha1/solver_types.go b/apis/nodecore/v1alpha1/solver_types.go index 22accff..533ed98 100644 --- a/apis/nodecore/v1alpha1/solver_types.go +++ b/apis/nodecore/v1alpha1/solver_types.go @@ -80,7 +80,7 @@ type RangeSelector struct { type SolverSpec struct { // Selector contains the flavour requirements for the solver. - Selector FlavourSelector `json:"selector"` + 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. diff --git a/apis/nodecore/v1alpha1/zz_generated.deepcopy.go b/apis/nodecore/v1alpha1/zz_generated.deepcopy.go index 4ea1249..4f86197 100644 --- a/apis/nodecore/v1alpha1/zz_generated.deepcopy.go +++ b/apis/nodecore/v1alpha1/zz_generated.deepcopy.go @@ -495,7 +495,11 @@ func (in *SolverList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SolverSpec) DeepCopyInto(out *SolverSpec) { *out = *in - in.Selector.DeepCopyInto(&out.Selector) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(FlavourSelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SolverSpec. diff --git a/apis/reservation/v1alpha1/contract_types.go b/apis/reservation/v1alpha1/contract_types.go index 7e2dd65..cab5cd8 100644 --- a/apis/reservation/v1alpha1/contract_types.go +++ b/apis/reservation/v1alpha1/contract_types.go @@ -41,7 +41,7 @@ type ContractSpec struct { // 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"` + Partition *Partition `json:"partition,omitempty"` // This is the Node identity of the buyer FLUIDOS Node. Buyer nodecorev1alpha1.NodeIdentity `json:"buyer"` diff --git a/apis/reservation/v1alpha1/reservation_types.go b/apis/reservation/v1alpha1/reservation_types.go index 2c1b837..4705be0 100644 --- a/apis/reservation/v1alpha1/reservation_types.go +++ b/apis/reservation/v1alpha1/reservation_types.go @@ -46,7 +46,7 @@ type ReservationSpec struct { Seller nodecorev1alpha1.NodeIdentity `json:"seller"` // Parition is the partition of the flavour that is being reserved - Partition Partition `json:"partition,omitempty"` + Partition *Partition `json:"partition,omitempty"` // Reserve indicates if the reservation is a reserve or not Reserve bool `json:"reserve,omitempty"` diff --git a/apis/reservation/v1alpha1/transaction_types.go b/apis/reservation/v1alpha1/transaction_types.go index dc26a29..dbe6e58 100644 --- a/apis/reservation/v1alpha1/transaction_types.go +++ b/apis/reservation/v1alpha1/transaction_types.go @@ -31,7 +31,7 @@ type TransactionSpec struct { ClusterID string `json:"clusterID"` // Partition is the partition of the flavour that is being reserved - Partition Partition `json:"partition,omitempty"` + Partition *Partition `json:"partition,omitempty"` // StartTime is the time at which the reservation should start StartTime string `json:"startTime,omitempty"` diff --git a/apis/reservation/v1alpha1/zz_generated.deepcopy.go b/apis/reservation/v1alpha1/zz_generated.deepcopy.go index 9a77e86..bbcfeef 100644 --- a/apis/reservation/v1alpha1/zz_generated.deepcopy.go +++ b/apis/reservation/v1alpha1/zz_generated.deepcopy.go @@ -85,7 +85,11 @@ func (in *ContractList) DeepCopyObject() runtime.Object { func (in *ContractSpec) DeepCopyInto(out *ContractSpec) { *out = *in in.Flavour.DeepCopyInto(&out.Flavour) - in.Partition.DeepCopyInto(&out.Partition) + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(Partition) + (*in).DeepCopyInto(*out) + } out.Buyer = in.Buyer out.Seller = in.Seller out.SellerCredentials = in.SellerCredentials @@ -223,7 +227,11 @@ func (in *ReservationSpec) DeepCopyInto(out *ReservationSpec) { *out = *in out.Buyer = in.Buyer out.Seller = in.Seller - in.Partition.DeepCopyInto(&out.Partition) + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(Partition) + (*in).DeepCopyInto(*out) + } out.PeeringCandidate = in.PeeringCandidate } @@ -317,7 +325,11 @@ func (in *TransactionList) DeepCopyObject() runtime.Object { func (in *TransactionSpec) DeepCopyInto(out *TransactionSpec) { *out = *in out.Buyer = in.Buyer - in.Partition.DeepCopyInto(&out.Partition) + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(Partition) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransactionSpec. diff --git a/cmd/rear-controller/main.go b/cmd/rear-controller/main.go index fb1686d..9f5beec 100644 --- a/cmd/rear-controller/main.go +++ b/cmd/rear-controller/main.go @@ -144,20 +144,39 @@ func main() { 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.StartHttpServer() - }() + /* go func() { + gw.Start() + }() */ // Start the REAR GRPC server - go func() { + /* go func() { grpcServer.Start() - }() + }() */ // TODO: Uncomment this when the webhook is ready. For now it does not work (Ale) // pcv := discoverymanager.NewPCValidator(mgr.GetClient()) diff --git a/deployments/node/Chart.yaml b/deployments/node/Chart.yaml index a8d8c7d..4188577 100644 --- a/deployments/node/Chart.yaml +++ b/deployments/node/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: fluidos +name: node description: A Helm chart for Fluidos Node # A chart can be either an 'application' or a 'library' chart. @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.0.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.1.0" +appVersion: "0.0.1" diff --git a/deployments/node/README.md b/deployments/node/README.md index 11d5c6c..a673a1d 100644 --- a/deployments/node/README.md +++ b/deployments/node/README.md @@ -1,4 +1,4 @@ -# Fluidos +# fluidos ![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.0](https://img.shields.io/badge/AppVersion-0.1.0-informational?style=flat-square) @@ -18,28 +18,28 @@ A Helm chart for Fluidos Node | localResourceManager.config.flavour.memoryStep | string | `"100Mi"` | The memory step that must be respected when requesting a flavour through a Flavour Selector. | | localResourceManager.config.nodeResourceLabel | string | `"node-role.fluidos.eu/resources"` | Label used to identify the nodes from which resources are collected. | | localResourceManager.config.resourceType | string | `"k8s-fluidos"` | This flag defines the resource type of the generated flavours. | -| localResourceManager.imageName | string | `"cannarelladev/local-resource-manager"` | | +| localResourceManager.imageName | string | `"ghcr.io/fluidos-project/local-resource-manager"` | | | localResourceManager.pod.annotations | object | `{}` | Annotations for the local-resource-manager pod. | | localResourceManager.pod.extraArgs | list | `[]` | Extra arguments for the local-resource-manager pod. | | localResourceManager.pod.labels | object | `{}` | Labels for the local-resource-manager pod. | | localResourceManager.pod.resources | object | `{"limits":{},"requests":{}}` | Resource requests and limits (https://kubernetes.io/docs/user-guide/compute-resources/) for the local-resource-manager pod. | | localResourceManager.replicas | int | `1` | The number of REAR Controller, which can be increased for active/passive high availability. | -| networkManager.configMaps.nodeIdentity.domain | string | `"fluidos.eu"` | The domain name of the FLUIDOS closed domani: It represents for instance the Enterprise and it is used to generate the FQDN of the owned FLUIDOS Nodes | +| networkManager.configMaps.nodeIdentity.domain | string | `""` | The domain name of the FLUIDOS closed domani: It represents for instance the Enterprise and it is used to generate the FQDN of the owned FLUIDOS Nodes | | networkManager.configMaps.nodeIdentity.ip | string | `nil` | The IP address of the FLUIDOS Node. It can be public or private, depending on the network configuration and it corresponds to the IP address to reach the Network Manager from the outside of the cluster. | | networkManager.configMaps.nodeIdentity.name | string | `"fluidos-network-manager-identity"` | The name of the ConfigMap containing the FLUIDOS Node identity info. | | networkManager.configMaps.nodeIdentity.nodeID | string | `nil` | The NodeID is a UUID that identifies the FLUIDOS Node. It is used to generate the FQDN of the owned FLUIDOS Nodes and it is unique in the FLUIDOS closed domain | | networkManager.configMaps.providers.default | string | `nil` | The IP List of SuperNodes separated by commas. | -| networkManager.configMaps.providers.local | string | `"192.168.0.1,192.168.0.2,192.168.0.3"` | The IP List of Local knwon FLUIDOS Nodes separated by commas. | +| networkManager.configMaps.providers.local | string | `""` | The IP List of Local knwon FLUIDOS Nodes separated by commas. | | networkManager.configMaps.providers.name | string | `"fluidos-network-manager-config"` | The name of the ConfigMap containing the list of the FLUIDOS Providers and the default FLUIDOS Provider (SuperNode or Catalogue). | | networkManager.configMaps.providers.remote | string | `nil` | The IP List of Remote known FLUIDOS Nodes separated by commas. | -| networkManager.imageName | string | `"ghcr.io/fluidos/network-manager"` | | +| networkManager.imageName | string | `"ghcr.io/fluidos-project/network-manager"` | | | networkManager.pod.annotations | object | `{}` | Annotations for the network-manager pod. | | networkManager.pod.extraArgs | list | `[]` | Extra arguments for the network-manager pod. | | networkManager.pod.labels | object | `{}` | Labels for the network-manager pod. | | networkManager.pod.resources | object | `{"limits":{},"requests":{}}` | Resource requests and limits (https://kubernetes.io/docs/user-guide/compute-resources/) for the network-manager pod. | | networkManager.replicas | int | `1` | The number of Network Manager, which can be increased for active/passive high availability. | | pullPolicy | string | `"IfNotPresent"` | The pullPolicy for fluidos-node pods. | -| rearController.imageName | string | `"cannarelladev/rear-controller"` | | +| rearController.imageName | string | `"ghcr.io/fluidos-project/rear-controller"` | | | rearController.pod.annotations | object | `{}` | Annotations for the rear-controller pod. | | rearController.pod.extraArgs | list | `[]` | Extra arguments for the rear-controller pod. | | rearController.pod.labels | object | `{}` | Labels for the rear-controller pod. | @@ -61,10 +61,13 @@ A Helm chart for Fluidos Node | rearController.service.grpc.port | int | `2710` | The gRPC port used by Liqo to connect with the Gateway of the rear-controller to obtain the Contract resources for a given consumer ClusterID. | | rearController.service.grpc.targetPort | int | `2710` | The target port used by the gRPC service. | | rearController.service.grpc.type | string | `"ClusterIP"` | Kubernetes service used to expose the gRPC Server to liqo. | -| rearManager.imageName | string | `"cannarelladev/rear-manager"` | | +| rearManager.imageName | string | `"ghcr.io/fluidos-project/rear-manager"` | | | rearManager.pod.annotations | object | `{}` | Annotations for the rear-manager pod. | | rearManager.pod.extraArgs | list | `[]` | Extra arguments for the rear-manager pod. | | rearManager.pod.labels | object | `{}` | Labels for the rear-manager pod. | | rearManager.pod.resources | object | `{"limits":{},"requests":{}}` | Resource requests and limits (https://kubernetes.io/docs/user-guide/compute-resources/) for the rear-manager pod. | | rearManager.replicas | int | `1` | The number of REAR Manager, which can be increased for active/passive high availability. | -| tag | string | `"v0.1"` | Images' tag to select a development version of fluidos-node instead of a release | +| tag | string | `""` | Images' tag to select a development version of fluidos-node instead of a release | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0) diff --git a/deployments/node/crds/nodecore.fluidos.eu_solvers.yaml b/deployments/node/crds/nodecore.fluidos.eu_solvers.yaml index f5895a7..d716922 100644 --- a/deployments/node/crds/nodecore.fluidos.eu_solvers.yaml +++ b/deployments/node/crds/nodecore.fluidos.eu_solvers.yaml @@ -201,7 +201,6 @@ spec: type: object required: - intentID - - selector type: object status: description: SolverStatus defines the observed state of Solver diff --git a/deployments/node/files/fluidos-rear-controller-ClusterRole.yaml b/deployments/node/files/fluidos-rear-controller-ClusterRole.yaml index 684e3f9..044e17c 100644 --- a/deployments/node/files/fluidos-rear-controller-ClusterRole.yaml +++ b/deployments/node/files/fluidos-rear-controller-ClusterRole.yaml @@ -59,6 +59,14 @@ rules: - get - list - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch - apiGroups: - nodecore.fluidos.eu resources: diff --git a/deployments/node/fluidos-0.1.0.tgz b/deployments/node/fluidos-0.1.0.tgz new file mode 100644 index 0000000..de8e744 Binary files /dev/null and b/deployments/node/fluidos-0.1.0.tgz differ diff --git a/deployments/node/index.yaml b/deployments/node/index.yaml new file mode 100644 index 0000000..02eabb3 --- /dev/null +++ b/deployments/node/index.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +entries: + node: + - apiVersion: v2 + appVersion: 0.0.1 + created: "2023-10-17T17:14:59.809373+02:00" + description: A Helm chart for Fluidos Node + digest: 8b51c13b01d65407087391bfada5f579cd2e9a1f668d4a0ce1c1178fe3bf4859 + name: node + type: application + urls: + - https://github.com/fluidos-project/node/deployments/node/node-0.0.1.tgz + version: 0.0.1 +generated: "2023-10-17T17:14:59.808177+02:00" diff --git a/deployments/node/node-0.0.1.tgz b/deployments/node/node-0.0.1.tgz new file mode 100644 index 0000000..27480dd Binary files /dev/null and b/deployments/node/node-0.0.1.tgz differ diff --git a/deployments/node/robots.tx b/deployments/node/robots.tx new file mode 100644 index 0000000..c6742d8 --- /dev/null +++ b/deployments/node/robots.tx @@ -0,0 +1,2 @@ +User-Agent: * +Disallow: / diff --git a/deployments/node/templates/fluidos-local-resource-manager-rbac.yaml b/deployments/node/templates/fluidos-local-resource-manager-rbac.yaml index a1fcee8..b991b4a 100644 --- a/deployments/node/templates/fluidos-local-resource-manager-rbac.yaml +++ b/deployments/node/templates/fluidos-local-resource-manager-rbac.yaml @@ -1,4 +1,3 @@ ---- {{- $resManagerConfig := (merge (dict "name" "local-resource-manager" "module" "local-resource-manager") .) -}} apiVersion: v1 diff --git a/deployments/node/templates/fluidos-rear-controller-rbac.yaml b/deployments/node/templates/fluidos-rear-controller-rbac.yaml index 2f4be40..2a70980 100644 --- a/deployments/node/templates/fluidos-rear-controller-rbac.yaml +++ b/deployments/node/templates/fluidos-rear-controller-rbac.yaml @@ -1,4 +1,3 @@ ---- {{- $rearControllerConfig := (merge (dict "name" "rear-controller" "module" "rear-controller") .) -}} apiVersion: v1 diff --git a/deployments/node/templates/fluidos-rear-manager-rbac.yaml b/deployments/node/templates/fluidos-rear-manager-rbac.yaml index 5c361bb..2c3e611 100644 --- a/deployments/node/templates/fluidos-rear-manager-rbac.yaml +++ b/deployments/node/templates/fluidos-rear-manager-rbac.yaml @@ -1,4 +1,3 @@ ---- {{- $rearManagerConfig := (merge (dict "name" "rear-manager" "module" "rear-manager") .) -}} apiVersion: v1 diff --git a/deployments/node/values.yaml b/deployments/node/values.yaml index adb7d4c..ba92994 100644 --- a/deployments/node/values.yaml +++ b/deployments/node/values.yaml @@ -3,7 +3,7 @@ # Declare variables to be passed into your templates. # -- Images' tag to select a development version of fluidos-node instead of a release -tag: "v0.1" +tag: "" # -- The pullPolicy for fluidos-node pods. pullPolicy: "IfNotPresent" @@ -31,7 +31,7 @@ localResourceManager: resources: limits: {} requests: {} - imageName: "cannarelladev/local-resource-manager" + imageName: "ghcr.io/fluidos-project/local-resource-manager" config: # -- Label used to identify the nodes from which resources are collected. nodeResourceLabel: "node-role.fluidos.eu/resources" @@ -61,7 +61,7 @@ rearManager: resources: limits: {} requests: {} - imageName: "cannarelladev/rear-manager" + imageName: "ghcr.io/fluidos-project/rear-manager" rearController: # -- The number of REAR Controller, which can be increased for active/passive high availability. @@ -77,7 +77,7 @@ rearController: resources: limits: {} requests: {} - imageName: "cannarelladev/rear-controller" + imageName: "ghcr.io/fluidos-project/rear-controller" service: grpc: name: "grpc" @@ -127,13 +127,13 @@ networkManager: limits: {} requests: {} # -- The resource image to be used by the network-manager pod. - imageName: "ghcr.io/fluidos/network-manager" + imageName: "ghcr.io/fluidos-project/network-manager" configMaps: providers: # -- The name of the ConfigMap containing the list of the FLUIDOS Providers and the default FLUIDOS Provider (SuperNode or Catalogue). name: "fluidos-network-manager-config" # -- The IP List of Local knwon FLUIDOS Nodes separated by commas. - local: "192.168.0.1,192.168.0.2,192.168.0.3" + local: "" # -- The IP List of Remote known FLUIDOS Nodes separated by commas. remote: # -- The IP List of SuperNodes separated by commas. @@ -142,7 +142,7 @@ networkManager: # -- The name of the ConfigMap containing the FLUIDOS Node identity info. name: "fluidos-network-manager-identity" # -- The domain name of the FLUIDOS closed domani: It represents for instance the Enterprise and it is used to generate the FQDN of the owned FLUIDOS Nodes - domain: "fluidos.eu" + domain: "" # -- The IP address of the FLUIDOS Node. It can be public or private, depending on the network configuration and it corresponds to the IP address to reach the Network Manager from the outside of the cluster. ip: # -- The NodeID is a UUID that identifies the FLUIDOS Node. It is used to generate the FQDN of the owned FLUIDOS Nodes and it is unique in the FLUIDOS closed domain diff --git a/examples/kind/consumer/values.yaml b/examples/kind/consumer/values.yaml index eaf4432..650253c 100644 --- a/examples/kind/consumer/values.yaml +++ b/examples/kind/consumer/values.yaml @@ -33,7 +33,7 @@ localResourceManager: resources: limits: {} requests: {} - imageName: "cannarelladev/local-resource-manager" + imageName: "ghcr.io/fluidos-project/local-resource-manager" config: # -- Label used to identify the nodes from which resources are collected. nodeResourceLabel: "node-role.fluidos.eu/resources" @@ -63,7 +63,7 @@ rearManager: resources: limits: {} requests: {} - imageName: "cannarelladev/rear-manager" + imageName: "ghcr.io/fluidos-project/rear-manager" rearController: # -- The number of REAR Controller, which can be increased for active/passive high availability. @@ -79,7 +79,7 @@ rearController: resources: limits: {} requests: {} - imageName: "cannarelladev/rear-controller" + imageName: "ghcr.io/fluidos-project/rear-controller" service: grpc: name: "grpc" diff --git a/examples/kind/setup.sh b/examples/kind/setup.sh index 3b4a0cd..0c2c893 100644 --- a/examples/kind/setup.sh +++ b/examples/kind/setup.sh @@ -32,6 +32,8 @@ helm install node ../../deployments/node -n fluidos \ --set networkManager.configMaps.nodeIdentity.ip=$consumer_controlplane_ip:$consumer_node_port \ --set networkManager.configMaps.providers.local=$provider_controlplane_ip:$provider_node_port +liqoctl install kind --cluster-name fluidos-consumer + export KUBECONFIG=$PWD/provider/config kubectl apply -f ../../deployments/node/crds @@ -49,4 +51,6 @@ helm install node ../../deployments/node -n fluidos \ --set networkManager.configMaps.nodeIdentity.ip=$provider_controlplane_ip:$provider_node_port \ --set networkManager.configMaps.providers.local=$consumer_controlplane_ip:$consumer_node_port +liqoctl install kind --cluster-name fluidos-provider + diff --git a/pkg/rear-controller/gateway/client.go b/pkg/rear-controller/gateway/client.go index d964f3b..88adda5 100644 --- a/pkg/rear-controller/gateway/client.go +++ b/pkg/rear-controller/gateway/client.go @@ -33,20 +33,28 @@ import ( // 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: reservation.Spec.Buyer.NodeID, - IP: reservation.Spec.Buyer.IP, - Domain: reservation.Spec.Buyer.Domain, + NodeID: g.ID.NodeID, + IP: g.ID.IP, + Domain: g.ID.Domain, }, - Partition: parseutil.ParsePartition(reservation.Spec.Partition), } 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 @@ -56,13 +64,13 @@ func (g *Gateway) ReserveFlavour(ctx context.Context, reservation *reservationv1 bodyBytes := bytes.NewBuffer(selectorBytes) url := fmt.Sprintf("http://%s%s%s", reservation.Spec.Seller.IP, RESERVE_FLAVOUR_PATH, flavourID) - klog.Infof("Sending POST request to %s", url) + klog.Infof("Sending request to %s", url) resp, err := makeRequest("POST", url, bodyBytes) - defer resp.Body.Close() if err != nil { return nil, err } + defer resp.Body.Close() // Check if the response status code is 200 (OK) if resp.StatusCode != http.StatusOK { @@ -82,7 +90,7 @@ func (g *Gateway) ReserveFlavour(ctx context.Context, reservation *reservationv1 return &transaction, fmt.Errorf("transactionID is empty") } - klog.Infof("Flavour %s reserved: transaction %v", flavourID, transaction) + klog.Infof("Flavour %s reserved: transaction ID %s", flavourID, transaction.TransactionID) g.addNewTransacion(transaction) @@ -91,6 +99,11 @@ func (g *Gateway) ReserveFlavour(ctx context.Context, reservation *reservationv1 // 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 @@ -113,10 +126,10 @@ func (g *Gateway) PurchaseFlavour(ctx context.Context, transactionID string, sel url := fmt.Sprintf("http://%s%s%s", seller.IP, PURCHASE_FLAVOUR_PATH, transactionID) resp, err := makeRequest("POST", url, bodyBytes) - defer resp.Body.Close() if err != nil { return nil, err } + defer resp.Body.Close() // Check if the response status code is 200 (OK) if resp.StatusCode != http.StatusOK { @@ -131,18 +144,24 @@ func (g *Gateway) PurchaseFlavour(ctx context.Context, transactionID string, sel } // 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) { - // Marshal the selector into JSON bytes - s := parseutil.ParseFlavourSelector(selector) +func (g *Gateway) DiscoverFlavours(selector *nodecorev1alpha1.FlavourSelector) ([]*nodecorev1alpha1.Flavour, error) { + err := checkLiqoReadiness(g.LiqoReady) + if err != nil { + return nil, err + } - // Create the Flavour CR from the first flavour in the array of Flavour + 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 _, ADDRESS := range providers { - flavour, err := searchFlavour(s, ADDRESS) + for _, provider := range providers { + flavour, err := discover(s, provider) if err != nil { klog.Errorf("Error when searching Flavour: %s", err) return nil, err @@ -153,3 +172,18 @@ func (g *Gateway) DiscoverFlavours(selector nodecorev1alpha1.FlavourSelector) ([ 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 +} diff --git a/pkg/rear-controller/gateway/gateway.go b/pkg/rear-controller/gateway/gateway.go index 31eca84..f100a19 100644 --- a/pkg/rear-controller/gateway/gateway.go +++ b/pkg/rear-controller/gateway/gateway.go @@ -16,16 +16,21 @@ package gateway import ( "context" + "fmt" "net/http" "time" "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" 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" ) @@ -36,6 +41,7 @@ import ( //+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" @@ -54,25 +60,46 @@ type Gateway struct { // 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: "", } } -// StartHttpServer starts a new HTTP server -func (g *Gateway) StartHttpServer() { +// 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_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") @@ -85,8 +112,11 @@ func (g *Gateway) StartHttpServer() { // Start server HTTP klog.Infof("Starting HTTP server on port %s", flags.HTTP_PORT) - klog.Fatal(srv.ListenAndServe()) + return srv.ListenAndServe() +} +func (g *Gateway) RegisterNodeIdentity(nodeIdentity *nodecorev1alpha1.NodeIdentity) { + g.ID = nodeIdentity } // Only for debugging purposes @@ -97,6 +127,17 @@ func (g *Gateway) StartHttpServer() { }) } */ +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) @@ -115,3 +156,36 @@ func (g *Gateway) refreshCache(ctx context.Context) (bool, error) { } 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 +} diff --git a/pkg/rear-controller/gateway/provider.go b/pkg/rear-controller/gateway/provider.go index 04e9249..917123e 100644 --- a/pkg/rear-controller/gateway/provider.go +++ b/pkg/rear-controller/gateway/provider.go @@ -43,6 +43,8 @@ import ( 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) @@ -50,20 +52,43 @@ func (g *Gateway) getFlavours(w http.ResponseWriter, r *http.Request) { return } - klog.Infof("Listing all the Flavour CRs: %d", len(flavours)) + 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:]...) + } + } - // Export the result as a list of Flavour struct (no CRs) - var flavoursParsed []models.Flavour + 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 { - flavoursParsed = append(flavoursParsed, parseutil.ParseFlavour(f)) + if f.Spec.Characteristics.Cpu.Cmp(max) == 1 { + max = f.Spec.Characteristics.Cpu + selected = f + } } - // Encode the FlavourList as JSON and write it to the response writer - encodeResponse(w, flavoursParsed) + 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) { +/* func (g *Gateway) getFlavourByID(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") // Get the flavourID from the URL @@ -89,13 +114,13 @@ func (g *Gateway) getFlavourByID(w http.ResponseWriter, r *http.Request) { // 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("Getting Flavours by selector...") + klog.Infof("Processing request for getting Flavours by selector...") // Read the request body body, err := io.ReadAll(r.Body) @@ -119,9 +144,9 @@ func (g *Gateway) getFlavoursBySelector(w http.ResponseWriter, r *http.Request) return } - klog.Infof("Listing all the Flavour CRs: %d", len(flavours)) + klog.Infof("Found %d Flavours in the cluster", len(flavours)) - // Filter flavours from array maintaining only the Spec.Available == true + // Filtering only the available flavours for i, f := range flavours { if !f.Spec.OptionalFields.Availability { flavours = append(flavours[:i], flavours[i+1:]...) @@ -172,7 +197,7 @@ func (g *Gateway) getFlavoursBySelector(w http.ResponseWriter, r *http.Request) klog.Infof("Flavour parsed: %v", parsed) - // Encode the FlavourList as JSON and write it to the response writer + // Encode the Flavour as JSON and write it to the response writer encodeResponse(w, parsed) } diff --git a/pkg/rear-controller/gateway/services.go b/pkg/rear-controller/gateway/services.go index 7520f5f..4dbe5a0 100644 --- a/pkg/rear-controller/gateway/services.go +++ b/pkg/rear-controller/gateway/services.go @@ -27,8 +27,7 @@ import ( "github.com/fluidos-project/node/pkg/utils/resourceforge" ) -// TODO: This function does not work without a selector, it should do a GET request to the seller -func searchFlavour(selector models.Selector, addr string) (*nodecorev1alpha1.Flavour, error) { +func searchFlavourWithSelector(selector *models.Selector, addr string) (*nodecorev1alpha1.Flavour, error) { var flavour models.Flavour // Marshal the selector into JSON bytes @@ -60,10 +59,39 @@ func searchFlavour(selector models.Selector, addr string) (*nodecorev1alpha1.Fla 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) diff --git a/pkg/rear-controller/gateway/utils.go b/pkg/rear-controller/gateway/utils.go index a1d1827..c96b8b0 100644 --- a/pkg/rear-controller/gateway/utils.go +++ b/pkg/rear-controller/gateway/utils.go @@ -31,14 +31,14 @@ import ( ) // buildSelector builds a selector from a request body -func buildSelector(body []byte) (models.Selector, error) { +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 &models.Selector{}, err } - return selector, nil + return &selector, nil } // getTransaction returns a transaction from the transactions map @@ -89,9 +89,9 @@ func encodeResponse(w http.ResponseWriter, data interface{}) { } const ( - authTokenSecretNamePrefix = "remote-token-" + //authTokenSecretNamePrefix = "remote-token-" - tokenKey = "token" + //tokenKey = "token" liqoNamespace = "liqo" ) diff --git a/pkg/rear-controller/grpc/liqo-resource-manager.go b/pkg/rear-controller/grpc/liqo-resource-manager.go index d5372d2..0d9e206 100644 --- a/pkg/rear-controller/grpc/liqo-resource-manager.go +++ b/pkg/rear-controller/grpc/liqo-resource-manager.go @@ -46,21 +46,20 @@ func NewGrpcServer(cl client.Client) *grpcServer { } } -func (s *grpcServer) Start() { +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 { - log.Fatalf("gRPC failed to listen: %v", err) + 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 - if err := s.Server.Serve(lis); err != nil { - log.Fatalf("gRPC failed to serve: %v", err) - } + return s.Server.Serve(lis) } /* func (s *grpcServer) RegisterContractHandler(ch connector.ContractHandler) { @@ -92,7 +91,7 @@ func (s *grpcServer) Subscribe(req *resourcemonitors.Empty, srv resourcemonitors fmt.Println("Subscribe") - s.NotifyChange(context.Background(), &resourcemonitors.ClusterIdentity{ClusterID: resourcemonitors.AllClusterIDs}) + _ = s.NotifyChange(context.Background(), &resourcemonitors.ClusterIdentity{ClusterID: resourcemonitors.AllClusterIDs}) for { select { @@ -101,6 +100,7 @@ func (s *grpcServer) Subscribe(req *resourcemonitors.Empty, srv resourcemonitors return nil } } + } func (s *grpcServer) NotifyChange(ctx context.Context, req *resourcemonitors.ClusterIdentity) error { @@ -108,7 +108,7 @@ func (s *grpcServer) NotifyChange(ctx context.Context, req *resourcemonitors.Clu if s.stream == nil { return fmt.Errorf("you must first subscribe a controller manager to notify a change") } else { - s.stream.Send(req) + _ = s.stream.Send(req) } return nil } @@ -128,5 +128,5 @@ func (s *grpcServer) GetOfferResourcesByClusterID(clusterID string) (*corev1.Res } func (s *grpcServer) UpdatePeeringOffer(clusterID string) { - s.NotifyChange(context.Background(), &resourcemonitors.ClusterIdentity{ClusterID: clusterID}) + _ = s.NotifyChange(context.Background(), &resourcemonitors.ClusterIdentity{ClusterID: clusterID}) } diff --git a/pkg/rear-controller/grpc/service.go b/pkg/rear-controller/grpc/service.go index 25d1a83..0179aa9 100644 --- a/pkg/rear-controller/grpc/service.go +++ b/pkg/rear-controller/grpc/service.go @@ -59,7 +59,7 @@ func multipleContractLogic(contracts []reservationv1alpha1.Contract) *corev1.Res } // This function adds the resources of a contract to the existing resourceList -func addResources(resources corev1.ResourceList, partition reservationv1alpha1.Partition) *corev1.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 @@ -71,7 +71,7 @@ func addResources(resources corev1.ResourceList, partition reservationv1alpha1.P return &resources } -func mapQuantityToResourceList(partition reservationv1alpha1.Partition) *corev1.ResourceList { +func mapQuantityToResourceList(partition *reservationv1alpha1.Partition) *corev1.ResourceList { return &corev1.ResourceList{ corev1.ResourceCPU: partition.Cpu, corev1.ResourceMemory: partition.Memory, diff --git a/pkg/rear-manager/solver_controller.go b/pkg/rear-manager/solver_controller.go index 5a610f7..3dc666a 100644 --- a/pkg/rear-manager/solver_controller.go +++ b/pkg/rear-manager/solver_controller.go @@ -215,6 +215,7 @@ func (r *SolverReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr 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 @@ -226,8 +227,10 @@ func (r *SolverReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, err } - // Forge the Partition - partition := resourceforge.ForgePartition(solver.Spec.Selector) + if solver.Spec.Selector == nil { + // Forge the Partition + partition = resourceforge.ForgePartition(solver.Spec.Selector) + } // Get the NodeIdentity nodeIdentity := getters.GetNodeIdentity(ctx, r.Client) @@ -313,6 +316,7 @@ func (r *SolverReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr 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) @@ -357,7 +361,7 @@ func (r *SolverReconciler) searchPeeringCandidates(ctx context.Context, solver * // Filter the list of PeeringCandidates based on the Flavour Selector for _, p := range filtered { - res := common.FilterPeeringCandidate(&selector, &p) + res := common.FilterPeeringCandidate(selector, &p) if res { result = append(result, p) } diff --git a/pkg/utils/common/common.go b/pkg/utils/common/common.go index 82d62f7..d10f540 100644 --- a/pkg/utils/common/common.go +++ b/pkg/utils/common/common.go @@ -28,7 +28,7 @@ import ( ) // FilterFlavoursBySelector returns the Flavour CRs in the cluster that match the selector -func FilterFlavoursBySelector(flavours []nodecorev1alpha1.Flavour, selector models.Selector) ([]nodecorev1alpha1.Flavour, error) { +func FilterFlavoursBySelector(flavours []nodecorev1alpha1.Flavour, selector *models.Selector) ([]nodecorev1alpha1.Flavour, error) { var flavoursSelected []nodecorev1alpha1.Flavour // Get the Flavours that match the selector @@ -46,7 +46,7 @@ func FilterFlavoursBySelector(flavours []nodecorev1alpha1.Flavour, selector mode } // filterFlavour filters the Flavour CRs in the cluster that match the selector -func FilterFlavour(selector models.Selector, f nodecorev1alpha1.Flavour) bool { +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) @@ -54,27 +54,27 @@ func FilterFlavour(selector models.Selector, f nodecorev1alpha1.Flavour) bool { } if selector.MatchSelector != nil { - if selector.MatchSelector.Cpu == 0 && f.Spec.Characteristics.Cpu.CmpInt64(int64(selector.MatchSelector.Cpu)) != 0 { + 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 == 0 && f.Spec.Characteristics.Memory.CmpInt64(int64(selector.MatchSelector.Memory)) != 0 { + 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 == 0 && f.Spec.Characteristics.EphemeralStorage.CmpInt64(int64(selector.MatchSelector.EphemeralStorage)) < 0 { + 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 == 0 && f.Spec.Characteristics.PersistentStorage.CmpInt64(int64(selector.MatchSelector.Storage)) < 0 { + 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 == 0 && f.Spec.Characteristics.Gpu.CmpInt64(int64(selector.MatchSelector.Gpu)) < 0 { + 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 } @@ -82,47 +82,47 @@ func FilterFlavour(selector models.Selector, f nodecorev1alpha1.Flavour) bool { if selector.RangeSelector != nil && selector.MatchSelector == nil { - if selector.RangeSelector.MinCpu != 0 && f.Spec.Characteristics.Cpu.CmpInt64(int64(selector.RangeSelector.MinCpu)) < 0 { + 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 != 0 && f.Spec.Characteristics.Memory.CmpInt64(int64(selector.RangeSelector.MinMemory)) < 0 { + 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 != 0 && f.Spec.Characteristics.EphemeralStorage.CmpInt64(int64(selector.RangeSelector.MinEph)) < 0 { + 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 != 0 && f.Spec.Characteristics.PersistentStorage.CmpInt64(int64(selector.RangeSelector.MinStorage)) < 0 { + 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 != 0 && f.Spec.Characteristics.Gpu.CmpInt64(int64(selector.RangeSelector.MinGpu)) < 0 { + if selector.RangeSelector.MinGpu.CmpInt64(0) != 0 && f.Spec.Characteristics.Gpu.Cmp(selector.RangeSelector.MinGpu) < 0 { return false } - if selector.RangeSelector.MaxCpu != 0 && f.Spec.Characteristics.Cpu.CmpInt64(int64(selector.RangeSelector.MaxCpu)) > 0 { + if selector.RangeSelector.MaxCpu.CmpInt64(0) != 0 && f.Spec.Characteristics.Cpu.Cmp(selector.RangeSelector.MaxCpu) > 0 { return false } - if selector.RangeSelector.MaxMemory != 0 && f.Spec.Characteristics.Memory.CmpInt64(int64(selector.RangeSelector.MaxMemory)) > 0 { + if selector.RangeSelector.MaxMemory.CmpInt64(0) != 0 && f.Spec.Characteristics.Memory.Cmp(selector.RangeSelector.MaxMemory) > 0 { return false } - if selector.RangeSelector.MaxEph != 0 && f.Spec.Characteristics.EphemeralStorage.CmpInt64(int64(selector.RangeSelector.MaxEph)) > 0 { + if selector.RangeSelector.MaxEph.CmpInt64(0) != 0 && f.Spec.Characteristics.EphemeralStorage.Cmp(selector.RangeSelector.MaxEph) > 0 { return false } - if selector.RangeSelector.MaxStorage != 0 && f.Spec.Characteristics.PersistentStorage.CmpInt64(int64(selector.RangeSelector.MaxStorage)) > 0 { + if selector.RangeSelector.MaxStorage.CmpInt64(0) != 0 && f.Spec.Characteristics.PersistentStorage.Cmp(selector.RangeSelector.MaxStorage) > 0 { return false } - if selector.RangeSelector.MaxGpu != 0 && f.Spec.Characteristics.Gpu.CmpInt64(int64(selector.RangeSelector.MaxGpu)) > 0 { + if selector.RangeSelector.MaxGpu.CmpInt64(0) != 0 && f.Spec.Characteristics.Gpu.Cmp(selector.RangeSelector.MaxGpu) > 0 { return false } } @@ -132,13 +132,13 @@ func FilterFlavour(selector models.Selector, f nodecorev1alpha1.Flavour) bool { // 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) + 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 { +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") diff --git a/pkg/utils/consts/consts.go b/pkg/utils/consts/consts.go index c5d861e..99fd8bb 100644 --- a/pkg/utils/consts/consts.go +++ b/pkg/utils/consts/consts.go @@ -17,4 +17,6 @@ package consts const ( NETWORK_CONFIG_MAP_NAME = "fluidos-network-manager-config" NODE_IDENTITY_CONFIG_MAP_NAME = "fluidos-network-manager-identity" + LIQO_CLUSTERID_CONFIGMAP_NAME = "liqo-clusterid-configmap" + LIQO_NAMESPACE = "liqo" ) diff --git a/pkg/utils/flags/flags.go b/pkg/utils/flags/flags.go index 2f2b0f9..32eddd6 100644 --- a/pkg/utils/flags/flags.go +++ b/pkg/utils/flags/flags.go @@ -28,6 +28,7 @@ var ( EXPIRATION_TRANSACTION = 20 * time.Second EXPIRATION_CONTRACT = 365 * 24 * time.Hour REFRESH_CACHE_INTERVAL = 20 * time.Second + LIQO_CHECK_INTERVAL = 20 * time.Second ) var ( diff --git a/pkg/utils/models/gateway.go b/pkg/utils/models/gateway.go index 32df4ff..364a03b 100644 --- a/pkg/utils/models/gateway.go +++ b/pkg/utils/models/gateway.go @@ -30,5 +30,5 @@ type ReserveRequest struct { FlavourID string `json:"flavourID"` Buyer NodeIdentity `json:"buyerID"` ClusterID string `json:"clusterID"` - Partition Partition `json:"partition"` + Partition *Partition `json:"partition,omitempty"` } diff --git a/pkg/utils/models/models.go b/pkg/utils/models/models.go index a32a617..a741faf 100644 --- a/pkg/utils/models/models.go +++ b/pkg/utils/models/models.go @@ -16,6 +16,8 @@ package models import ( "time" + + "k8s.io/apimachinery/pkg/api/resource" ) // Flavour represents a Flavour object with its characteristics and policies. @@ -33,12 +35,12 @@ type Flavour struct { // Characteristics represents the characteristics of a Flavour, such as CPU and RAM. type Characteristics struct { - CPU int `json:"cpu,omitempty"` - Memory int `json:"memory,omitempty"` - PersistentStorage int `json:"storage,omitempty"` - EphemeralStorage int `json:"ephemeralStorage,omitempty"` - GPU int `json:"gpu,omitempty"` - Architecture string `json:"architecture,omitempty"` + 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. @@ -49,10 +51,10 @@ type Policy struct { // Partitionable represents the partitioning properties of a Flavour, such as the minimum and incremental values of CPU and RAM. type Partitionable struct { - CPUMinimum int `json:"cpuMinimum"` - MemoryMinimum int `json:"memoryMinimum"` - CPUStep int `json:"cpuStep"` - MemoryStep int `json:"memoryStep"` + 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. @@ -90,23 +92,23 @@ type Selector struct { // MatchSelector represents the criteria for selecting Flavours through a strict match. type MatchSelector struct { - Cpu int `json:"cpu,omitempty"` - Memory int `json:"memory,omitempty"` - Storage int `json:"storage,omitempty"` - EphemeralStorage int `json:"ephemeralStorage,omitempty"` - Gpu int `json:"gpu,omitempty"` + 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 int `json:"minCpu,omitempty"` - MinMemory int `json:"minMemory,omitempty"` - MinStorage int `json:"minStorage,omitempty"` - MinEph int `json:"minEph,omitempty"` - MinGpu int `json:"minGpu,omitempty"` - MaxCpu int `json:"maxCpu,omitempty"` - MaxMemory int `json:"maxMemory,omitempty"` - MaxStorage int `json:"maxStorage,omitempty"` - MaxEph int `json:"maxEph,omitempty"` - MaxGpu int `json:"maxGpu,omitempty"` + 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"` } diff --git a/pkg/utils/models/reservation.go b/pkg/utils/models/reservation.go index fcfff55..7b7e5af 100644 --- a/pkg/utils/models/reservation.go +++ b/pkg/utils/models/reservation.go @@ -14,21 +14,23 @@ package models +import "k8s.io/apimachinery/pkg/api/resource" + // Partition represents the partitioning properties of a Flavour type Partition struct { - Architecture string `json:"architecture"` - Cpu int `json:"cpu"` - Memory int `json:"memory"` - EphemeralStorage int `json:"ephemeral-storage,omitempty"` - Gpu int `json:"gpu,omitempty"` - Storage int `json:"storage,omitempty"` + Architecture string `json:"architecture"` + Cpu resource.Quantity `json:"cpu"` + Memory resource.Quantity `json:"memory"` + EphemeralStorage resource.Quantity `json:"ephemeral-storage,omitempty"` + Gpu resource.Quantity `json:"gpu,omitempty"` + Storage resource.Quantity `json:"storage,omitempty"` } // Transaction contains information regarding the transaction for a flavour type Transaction struct { TransactionID string `json:"transactionID"` FlavourID string `json:"flavourID"` - Partition Partition `json:"partition"` + Partition *Partition `json:"partition,omitempty"` Buyer NodeIdentity `json:"buyer"` ClusterID string `json:"clusterID"` StartTime string `json:"startTime"` @@ -45,7 +47,7 @@ type Contract struct { SellerCredentials LiqoCredentials `json:"sellerCredentials"` ExpirationTime string `json:"expirationTime,omitempty"` ExtraInformation map[string]string `json:"extraInformation,omitempty"` - Partition Partition `json:"partition,omitempty"` + Partition *Partition `json:"partition,omitempty"` } // LiqoCredentials contains the credentials of a Liqo cluster to enstablish a peering. diff --git a/pkg/utils/parseutil/parseutil.go b/pkg/utils/parseutil/parseutil.go index a453ba3..9d46037 100644 --- a/pkg/utils/parseutil/parseutil.go +++ b/pkg/utils/parseutil/parseutil.go @@ -22,88 +22,58 @@ import ( ) // ParseFlavourSelector parses FlavourSelector into a Selector -func ParseFlavourSelector(selector nodecorev1alpha1.FlavourSelector) (s models.Selector) { +func ParseFlavourSelector(selector *nodecorev1alpha1.FlavourSelector) (s *models.Selector) { s.Architecture = selector.Architecture s.FlavourType = string(selector.FlavourType) if selector.MatchSelector != nil { - cpu, _ := selector.MatchSelector.Cpu.AsInt64() - memory, _ := selector.MatchSelector.Memory.AsInt64() - ephStorage, _ := selector.MatchSelector.EphemeralStorage.AsInt64() - storage, _ := selector.MatchSelector.Storage.AsInt64() - gpu, _ := selector.MatchSelector.Gpu.AsInt64() - s.MatchSelector = &models.MatchSelector{ - Cpu: int(cpu), - Memory: int(memory), - EphemeralStorage: int(ephStorage), - Storage: int(storage), - Gpu: int(gpu), + Cpu: selector.MatchSelector.Cpu, + Memory: selector.MatchSelector.Memory, + EphemeralStorage: selector.MatchSelector.EphemeralStorage, + Storage: selector.MatchSelector.Storage, + Gpu: selector.MatchSelector.Gpu, } } if selector.RangeSelector != nil { - minCpu, _ := selector.RangeSelector.MinCpu.AsInt64() - minMemory, _ := selector.RangeSelector.MinMemory.AsInt64() - minEph, _ := selector.RangeSelector.MinEph.AsInt64() - minStorage, _ := selector.RangeSelector.MinStorage.AsInt64() - minGpu, _ := selector.RangeSelector.MinGpu.AsInt64() - maxCpu, _ := selector.RangeSelector.MaxCpu.AsInt64() - maxMemory, _ := selector.RangeSelector.MaxMemory.AsInt64() - maxEph, _ := selector.RangeSelector.MaxEph.AsInt64() - maxStorage, _ := selector.RangeSelector.MaxStorage.AsInt64() - maxGpu, _ := selector.RangeSelector.MaxGpu.AsInt64() - s.RangeSelector = &models.RangeSelector{ - MinCpu: int(minCpu), - MinMemory: int(minMemory), - MinEph: int(minEph), - MinStorage: int(minStorage), - MinGpu: int(minGpu), - MaxCpu: int(maxCpu), - MaxMemory: int(maxMemory), - MaxEph: int(maxEph), - MaxStorage: int(maxStorage), - MaxGpu: int(maxGpu), + 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 { - cpu, _ := partition.Cpu.AsInt64() - memory, _ := partition.Memory.AsInt64() - ephStorage, _ := partition.EphemeralStorage.AsInt64() - storage, _ := partition.Storage.AsInt64() - gpu, _ := partition.Gpu.AsInt64() - - return models.Partition{ - Cpu: int(cpu), - Memory: int(memory), - EphemeralStorage: int(ephStorage), - Storage: int(storage), - Gpu: int(gpu), +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 { - p := reservationv1alpha1.Partition{ - Architecture: partition.Architecture, - Cpu: *resource.NewQuantity(int64(partition.Cpu), resource.DecimalSI), - Memory: *resource.NewQuantity(int64(partition.Memory), resource.BinarySI), - } - if partition.EphemeralStorage != 0 { - p.EphemeralStorage = *resource.NewQuantity(int64(partition.EphemeralStorage), resource.BinarySI) - } - if partition.Storage != 0 { - p.Storage = *resource.NewQuantity(int64(partition.Storage), resource.BinarySI) +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, } - if partition.Gpu != 0 { - p.Gpu = *resource.NewQuantity(int64(partition.Gpu), resource.DecimalSI) - } - return p } func ParseNodeIdentity(node nodecorev1alpha1.NodeIdentity) models.NodeIdentity { @@ -116,29 +86,26 @@ func ParseNodeIdentity(node nodecorev1alpha1.NodeIdentity) models.NodeIdentity { // ParseFlavourObject creates a Flavour Object from a Flavour CR func ParseFlavour(flavour nodecorev1alpha1.Flavour) models.Flavour { - cpu, _ := flavour.Spec.Characteristics.Cpu.AsInt64() - ram, _ := flavour.Spec.Characteristics.Memory.AsInt64() - cpuMin, _ := flavour.Spec.Policy.Partitionable.CpuMin.AsInt64() - memoryMin, _ := flavour.Spec.Policy.Partitionable.MemoryMin.AsInt64() - cpuStep, _ := flavour.Spec.Policy.Partitionable.CpuStep.AsInt64() - memoryStep, _ := flavour.Spec.Policy.Partitionable.MemoryStep.AsInt64() - obj := models.Flavour{ + return models.Flavour{ FlavourID: flavour.Name, Type: string(flavour.Spec.Type), ProviderID: flavour.Spec.ProviderID, Characteristics: models.Characteristics{ - CPU: int(cpu), - Memory: int(ram), + 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: int(cpuMin), - MemoryMinimum: int(memoryMin), - CPUStep: int(cpuStep), - MemoryStep: int(memoryStep), + 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 @@ -163,19 +130,6 @@ func ParseFlavour(flavour nodecorev1alpha1.Flavour) models.Flavour { WorkerID: flavour.Spec.OptionalFields.WorkerID, }, } - if ephStorage, ok := flavour.Spec.Characteristics.EphemeralStorage.AsInt64(); ok == true && ephStorage != 0 { - obj.Characteristics.EphemeralStorage = int(ephStorage) - } - if storage, ok := flavour.Spec.Characteristics.PersistentStorage.AsInt64(); ok == true && storage != 0 { - obj.Characteristics.PersistentStorage = int(storage) - } - if gpu, ok := flavour.Spec.Characteristics.Gpu.AsInt64(); ok == true && gpu != 0 { - obj.Characteristics.GPU = int(gpu) - } - if flavour.Spec.Characteristics.Architecture != "" { - obj.Characteristics.Architecture = flavour.Spec.Characteristics.Architecture - } - return obj } // ForgeContractObject creates a Contract Object diff --git a/pkg/utils/resourceforge/forge.go b/pkg/utils/resourceforge/forge.go index cca9411..5defe30 100644 --- a/pkg/utils/resourceforge/forge.go +++ b/pkg/utils/resourceforge/forge.go @@ -17,7 +17,6 @@ package resourceforge import ( "time" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" @@ -31,14 +30,19 @@ import ( ) // ForgeDiscovery creates a Discovery CR from a FlavourSelector and a solverID -func ForgeDiscovery(selector nodecorev1alpha1.FlavourSelector, solverID string) *advertisementv1alpha1.Discovery { +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: selector, + Selector: func() *nodecorev1alpha1.FlavourSelector { + if selector != nil { + return selector + } + return nil + }(), SolverID: solverID, Subscribe: false, }, @@ -72,9 +76,9 @@ func ForgePeeringCandidate(flavourPeeringCandidate *nodecorev1alpha1.Flavour, so } // ForgeReservation creates a Reservation CR from a PeeringCandidate -func ForgeReservation(peeringCandidate advertisementv1alpha1.PeeringCandidate, partition reservationv1alpha1.Partition, ni nodecorev1alpha1.NodeIdentity) *reservationv1alpha1.Reservation { +func ForgeReservation(peeringCandidate advertisementv1alpha1.PeeringCandidate, partition *reservationv1alpha1.Partition, ni nodecorev1alpha1.NodeIdentity) *reservationv1alpha1.Reservation { solverID := peeringCandidate.Spec.SolverID - return &reservationv1alpha1.Reservation{ + reservation := &reservationv1alpha1.Reservation{ ObjectMeta: metav1.ObjectMeta{ Name: namings.ForgeReservationName(solverID), Namespace: flags.FLUIDOS_NAMESPACE, @@ -91,11 +95,14 @@ func ForgeReservation(peeringCandidate advertisementv1alpha1.PeeringCandidate, p Name: peeringCandidate.Name, Namespace: peeringCandidate.Namespace, }, - Reserve: true, - Purchase: true, - Partition: partition, + Reserve: true, + Purchase: true, }, } + if partition != nil { + reservation.Spec.Partition = partition + } + return reservation } // ForgeContract creates a Contract CR @@ -116,8 +123,13 @@ func ForgeContract(flavour nodecorev1alpha1.Flavour, transaction models.Transact Seller: flavour.Spec.Owner, SellerCredentials: *lc, TransactionID: transaction.TransactionID, - Partition: parseutil.ParsePartitionFromObj(transaction.Partition), - ExpirationTime: time.Now().Add(flags.EXPIRATION_CONTRACT).Format(time.RFC3339), + 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{ @@ -139,10 +151,12 @@ func ForgeFlavourFromMetrics(node models.NodeInfo, ni nodecorev1alpha1.NodeIdent ProviderID: ni.NodeID, Type: nodecorev1alpha1.K8S, Characteristics: nodecorev1alpha1.Characteristics{ - Architecture: node.Architecture, - Cpu: node.ResourceMetrics.CPUAvailable, - Memory: node.ResourceMetrics.MemoryAvailable, - EphemeralStorage: node.ResourceMetrics.EphemeralStorage, + 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{ @@ -197,7 +211,12 @@ func ForgeContractObj(contract *reservationv1alpha1.Contract) models.Contract { Token: contract.Spec.SellerCredentials.Token, Endpoint: contract.Spec.SellerCredentials.Endpoint, }, - Partition: parseutil.ParsePartition(contract.Spec.Partition), + 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 { @@ -243,8 +262,13 @@ func ForgeContractFromObj(contract models.Contract) *reservationv1alpha1.Contrac Token: contract.SellerCredentials.Token, Endpoint: contract.SellerCredentials.Endpoint, }, - TransactionID: contract.TransactionID, - Partition: parseutil.ParsePartitionFromObj(contract.Partition), + 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 { @@ -278,7 +302,12 @@ func ForgeTransactionFromObj(reservation *models.Transaction) *reservationv1alph NodeID: reservation.Buyer.NodeID, }, ClusterID: reservation.ClusterID, - Partition: parseutil.ParsePartitionFromObj(reservation.Partition), + Partition: func() *reservationv1alpha1.Partition { + if reservation.Partition != nil { + return parseutil.ParsePartitionFromObj(reservation.Partition) + } + return nil + }(), }, } } @@ -294,18 +323,22 @@ func ForgeFlavourFromObj(flavour models.Flavour) *nodecorev1alpha1.Flavour { ProviderID: flavour.Owner.NodeID, Type: nodecorev1alpha1.K8S, Characteristics: nodecorev1alpha1.Characteristics{ - Cpu: *resource.NewQuantity(int64(flavour.Characteristics.CPU), resource.DecimalSI), - Memory: *resource.NewQuantity(int64(flavour.Characteristics.Memory), resource.BinarySI), + 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: *resource.NewQuantity(int64(flavour.Policy.Partitionable.CPUMinimum), resource.DecimalSI), - MemoryMin: *resource.NewQuantity(int64(flavour.Policy.Partitionable.MemoryMinimum), resource.BinarySI), - CpuStep: *resource.NewQuantity(int64(flavour.Policy.Partitionable.CPUStep), resource.DecimalSI), - MemoryStep: *resource.NewQuantity(int64(flavour.Policy.Partitionable.MemoryStep), resource.BinarySI), + CpuMin: flavour.Policy.Partitionable.CPUMinimum, + MemoryMin: flavour.Policy.Partitionable.MemoryMinimum, + CpuStep: flavour.Policy.Partitionable.CPUStep, + MemoryStep: flavour.Policy.Partitionable.MemoryStep, } } return nil @@ -332,24 +365,11 @@ func ForgeFlavourFromObj(flavour models.Flavour) *nodecorev1alpha1.Flavour { }, }, } - - if flavour.Characteristics.EphemeralStorage != 0 { - f.Spec.Characteristics.EphemeralStorage = *resource.NewQuantity(int64(flavour.Characteristics.EphemeralStorage), resource.BinarySI) - } - if flavour.Characteristics.PersistentStorage != 0 { - f.Spec.Characteristics.PersistentStorage = *resource.NewQuantity(int64(flavour.Characteristics.PersistentStorage), resource.BinarySI) - } - if flavour.Characteristics.GPU != 0 { - f.Spec.Characteristics.Gpu = *resource.NewQuantity(int64(flavour.Characteristics.GPU), resource.DecimalSI) - } - if flavour.Characteristics.Architecture != "" { - f.Spec.Characteristics.Architecture = flavour.Characteristics.Architecture - } return f } -func ForgePartition(selector nodecorev1alpha1.FlavourSelector) reservationv1alpha1.Partition { - return reservationv1alpha1.Partition{ +func ForgePartition(selector *nodecorev1alpha1.FlavourSelector) *reservationv1alpha1.Partition { + return &reservationv1alpha1.Partition{ Architecture: selector.Architecture, Cpu: selector.RangeSelector.MinCpu, Memory: selector.RangeSelector.MinMemory,