diff --git a/Makefile b/Makefile index 8f326fce..f450d830 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ rbacs: controller-gen $(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_COMMAND) deployments/node/files/node-local-resource-manager-ClusterRole.yaml $(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_COMMAND) deployments/node/files/node-rear-manager-ClusterRole.yaml $(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_COMMAND) deployments/node/files/node-rear-controller-ClusterRole.yaml + $(CONTROLLER_GEN) paths="./pkg/network-manager/" rbac:roleName=node-network-manager output:rbac:stdout | awk -v RS="---\n" 'NR>1{f="./deployments/node/files/node-network-manager-" $$4 ".yaml";printf "%s",$$0 > f; close(f)}' && $(SED_COMMAND) deployments/node/files/node-network-manager-ClusterRole.yaml # Install gci if not available gci: diff --git a/apis/network/v1alpha1/groupversion_info.go b/apis/network/v1alpha1/groupversion_info.go new file mode 100644 index 00000000..aa7f3311 --- /dev/null +++ b/apis/network/v1alpha1/groupversion_info.go @@ -0,0 +1,34 @@ +// Copyright 2022-2024 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=network.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: "network.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 +) diff --git a/apis/network/v1alpha1/knowncluster_status.go b/apis/network/v1alpha1/knowncluster_status.go new file mode 100644 index 00000000..9c4d323d --- /dev/null +++ b/apis/network/v1alpha1/knowncluster_status.go @@ -0,0 +1,23 @@ +// Copyright 2022-2024 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" + +// UpdateStatus updates the status of the KnownCluster. +func (knowncluster *KnownCluster) UpdateStatus() { + knowncluster.Status.LastUpdateTime = tools.GetTimeNow() + knowncluster.Status.ExpirationTime = tools.GetExpirationTime(0, 0, 10) +} diff --git a/apis/network/v1alpha1/knowncluster_types.go b/apis/network/v1alpha1/knowncluster_types.go new file mode 100644 index 00000000..b976f7f6 --- /dev/null +++ b/apis/network/v1alpha1/knowncluster_types.go @@ -0,0 +1,62 @@ +// Copyright 2022-2024 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" +) + +// KnownClusterSpec defines the desired state of KnownCluster. +type KnownClusterSpec struct { + + // Address of the KnownCluster. + Address string `json:"address"` +} + +// KnownClusterStatus defines the observed state of KnownCluster. +type KnownClusterStatus struct { + + // This field represents the expiration time of the KnownCluster. It is used to determine when the KnownCluster is no longer valid. + ExpirationTime string `json:"expirationTime"` + + // This field represents the last update time of the KnownCluster. + LastUpdateTime string `json:"lastUpdateTime"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:shortName=kclust;kclusts + +// KnownCluster is the Schema for the clusters API. +type KnownCluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KnownClusterSpec `json:"spec,omitempty"` + Status KnownClusterStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// KnownClusterList contains a list of KnownCluster. +type KnownClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KnownCluster `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KnownCluster{}, &KnownClusterList{}) +} diff --git a/apis/network/v1alpha1/zz_generated.deepcopy.go b/apis/network/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000..7b74368f --- /dev/null +++ b/apis/network/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,112 @@ +//go:build !ignore_autogenerated + +// Copyright 2022-2024 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. + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KnownCluster) DeepCopyInto(out *KnownCluster) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KnownCluster. +func (in *KnownCluster) DeepCopy() *KnownCluster { + if in == nil { + return nil + } + out := new(KnownCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KnownCluster) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KnownClusterList) DeepCopyInto(out *KnownClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KnownCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KnownClusterList. +func (in *KnownClusterList) DeepCopy() *KnownClusterList { + if in == nil { + return nil + } + out := new(KnownClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KnownClusterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KnownClusterSpec) DeepCopyInto(out *KnownClusterSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KnownClusterSpec. +func (in *KnownClusterSpec) DeepCopy() *KnownClusterSpec { + if in == nil { + return nil + } + out := new(KnownClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KnownClusterStatus) DeepCopyInto(out *KnownClusterStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KnownClusterStatus. +func (in *KnownClusterStatus) DeepCopy() *KnownClusterStatus { + if in == nil { + return nil + } + out := new(KnownClusterStatus) + in.DeepCopyInto(out) + return out +} diff --git a/cmd/network-manager/doc.go b/cmd/network-manager/doc.go new file mode 100644 index 00000000..c5e3f0b3 --- /dev/null +++ b/cmd/network-manager/doc.go @@ -0,0 +1,16 @@ +// Copyright 2022-2024 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 is the entrypoint for the network manager +package main diff --git a/cmd/network-manager/main.go b/cmd/network-manager/main.go new file mode 100644 index 00000000..62bdbe0e --- /dev/null +++ b/cmd/network-manager/main.go @@ -0,0 +1,129 @@ +// Copyright 2022-2024 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" + "os" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + 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" + + networkv1alpha1 "github.com/fluidos-project/node/apis/network/v1alpha1" + nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" + networkmanager "github.com/fluidos-project/node/pkg/network-manager" +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(corev1.AddToScheme(scheme)) + utilruntime.Must(nodecorev1alpha1.AddToScheme(scheme)) + utilruntime.Must(networkv1alpha1.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.") + enableLocalDiscovery := flag.Bool("enable-local-discovery", true, "Enable discovery of other clusters on same LAN") + cniInterface := flag.String("cniInterface", "", "Name of the CNI virtual interface") + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + // Client + 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) + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: metricsAddr, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "a0b0c1d1.fluidos.eu", + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + // Print something about the mgr + setupLog.Info("Manager started", "manager", mgr) + + // Buffer for clusters multicast messages + nm := &networkmanager.NetworkManager{EnableLocalDiscovery: *enableLocalDiscovery} + + // Start the NetworkManager setup + if err := networkmanager.Setup(context.Background(), cl, nm, cniInterface); err != nil { + setupLog.Error(err, "Unable to setup NetworkManager") + os.Exit(1) + } + + // Start the NetworkManager extecution + if err := networkmanager.Execute(context.Background(), cl, nm); err != nil { + setupLog.Error(err, "Unable to execute NetworkManager") + os.Exit(1) + } + + // Register the controller + if err = (&networkmanager.KnownClusterReconciler{ + Client: cl, + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "KnownCluster") + os.Exit(1) + } + + // Register health checks + 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) + } + + // Start the NetworkManager reconcile + setupLog.Info("Starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} diff --git a/cmd/rear-controller/main.go b/cmd/rear-controller/main.go index 1b15038e..e5f10c31 100644 --- a/cmd/rear-controller/main.go +++ b/cmd/rear-controller/main.go @@ -34,6 +34,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" + networkv1alpha1 "github.com/fluidos-project/node/apis/network/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" @@ -53,6 +54,7 @@ func init() { utilruntime.Must(advertisementv1alpha1.AddToScheme(scheme)) utilruntime.Must(reservationv1alpha1.AddToScheme(scheme)) utilruntime.Must(nodecorev1alpha1.AddToScheme(scheme)) + utilruntime.Must(networkv1alpha1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/deployments/node/README.md b/deployments/node/README.md index 356cd17c..52e9b6aa 100644 --- a/deployments/node/README.md +++ b/deployments/node/README.md @@ -9,6 +9,10 @@ A Helm chart for Fluidos Node | Key | Type | Default | Description | |-----|------|---------|-------------| | common.affinity | object | `{}` | Affinity for all fluidos-node pods | +| common.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 | +| common.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. | +| common.configMaps.nodeIdentity.name | string | `"fluidos-node-identity"` | The name of the ConfigMap containing the FLUIDOS Node identity info. | +| common.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 | | common.extraArgs | list | `[]` | Extra arguments for all fluidos-node pods | | common.nodeSelector | object | `{}` | NodeSelector for all fluidos-node pods | | common.tolerations | list | `[]` | Tolerations for all fluidos-node pods | @@ -25,14 +29,11 @@ A Helm chart for Fluidos Node | 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 | `""` | 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 | `""` | 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.config.address.firstOctet | string | `"10"` | The first octet of the CNI virtual network subnet | +| networkManager.config.address.secondOctet | string | `nil` | The second octet of the CNI virtual network subnet | +| networkManager.config.address.thirdOctet | string | `nil` | The third octet of the CNI virtual network subnet | +| networkManager.config.multicast.address | string | `"239.11.11.1"` | | +| networkManager.config.multicast.port | int | `4000` | | | 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. | diff --git a/deployments/node/crds/network.fluidos.eu_knownclusters.yaml b/deployments/node/crds/network.fluidos.eu_knownclusters.yaml new file mode 100644 index 00000000..7bfe0e01 --- /dev/null +++ b/deployments/node/crds/network.fluidos.eu_knownclusters.yaml @@ -0,0 +1,69 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: knownclusters.network.fluidos.eu +spec: + group: network.fluidos.eu + names: + kind: KnownCluster + listKind: KnownClusterList + plural: knownclusters + shortNames: + - kclust + - kclusts + singular: knowncluster + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: KnownCluster is the Schema for the clusters API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: KnownClusterSpec defines the desired state of KnownCluster. + properties: + address: + description: Address of the KnownCluster. + type: string + required: + - address + type: object + status: + description: KnownClusterStatus defines the observed state of KnownCluster. + properties: + expirationTime: + description: This field represents the expiration time of the KnownCluster. + It is used to determine when the KnownCluster is no longer valid. + type: string + lastUpdateTime: + description: This field represents the last update time of the KnownCluster. + type: string + required: + - expirationTime + - lastUpdateTime + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/deployments/node/files/node-local-resource-manager-ClusterRole.yaml b/deployments/node/files/node-local-resource-manager-ClusterRole.yaml index 33fd3822..802aba7a 100644 --- a/deployments/node/files/node-local-resource-manager-ClusterRole.yaml +++ b/deployments/node/files/node-local-resource-manager-ClusterRole.yaml @@ -7,6 +7,14 @@ rules: - get - list - watch +- apiGroups: + - "" + resources: + - endpoints + verbs: + - get + - list + - watch - apiGroups: - "" resources: diff --git a/deployments/node/files/node-network-manager-ClusterRole.yaml b/deployments/node/files/node-network-manager-ClusterRole.yaml new file mode 100644 index 00000000..327aa536 --- /dev/null +++ b/deployments/node/files/node-network-manager-ClusterRole.yaml @@ -0,0 +1,37 @@ +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - endpoints + verbs: + - get + - list + - watch +- apiGroups: + - network.fluidos.eu + resources: + - knownclusters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - network.fluidos.eu + resources: + - knownclusters/status + verbs: + - get + - patch + - update diff --git a/deployments/node/files/node-rear-controller-ClusterRole.yaml b/deployments/node/files/node-rear-controller-ClusterRole.yaml index 1dad445f..7584d016 100644 --- a/deployments/node/files/node-rear-controller-ClusterRole.yaml +++ b/deployments/node/files/node-rear-controller-ClusterRole.yaml @@ -67,6 +67,14 @@ rules: - get - list - watch +- apiGroups: + - network.fluidos.eu + resources: + - knownclusters + verbs: + - get + - list + - watch - apiGroups: - nodecore.fluidos.eu resources: diff --git a/deployments/node/samples/knowncluster.yaml b/deployments/node/samples/knowncluster.yaml new file mode 100644 index 00000000..919cbf52 --- /dev/null +++ b/deployments/node/samples/knowncluster.yaml @@ -0,0 +1,13 @@ +apiVersion: network.fluidos.eu/v1alpha1 +kind: KnownCluster +metadata: + name: knowncluster-sample + namespace: fluidos +spec: + # Set ip:port with the provider cluster control plane + address: 172.8.0.2:30001 +status: + # Set these timestamps to a future time, otherwise the housekeeping will delete the CR + expirationTime: "2024-01-01T09:00:10Z" + lastUpdateTime: "2024-01-01T09:00:00Z" + \ No newline at end of file diff --git a/deployments/node/templates/fluidos-network-manager-configmap.yaml b/deployments/node/templates/fluidos-network-manager-configmap.yaml deleted file mode 100644 index 7d50b798..00000000 --- a/deployments/node/templates/fluidos-network-manager-configmap.yaml +++ /dev/null @@ -1,51 +0,0 @@ -{{- $networkManagerConfig := (merge (dict "name" "network-manager" "module" "network-manager") .) -}} - -apiVersion: v1 -kind: ConfigMap -metadata: - {{- if .Values.networkManager.pod.annotations }} - annotations: - {{- toYaml .Values.networkManager.pod.annotations | nindent 4 }} - {{- end}} - labels: - {{- include "fluidos.labels" $networkManagerConfig | nindent 4 }} - name: {{ .Values.networkManager.configMaps.providers.name }} - namespace: {{ .Release.Namespace }} -data: - {{ if .Values.networkManager.configMaps.providers.local }} - local: {{ .Values.networkManager.configMaps.providers.local }} - {{- end }} - {{ if .Values.networkManager.configMaps.providers.remote }} - remote: {{ .Values.networkManager.configMaps.providers.remote }} - {{- end }} - {{ if .Values.networkManager.configMaps.providers.default }} - default: {{ .Values.networkManager.configMaps.providers.default }} - {{- end }} ---- -apiVersion: v1 -kind: ConfigMap -metadata: - {{- if .Values.networkManager.pod.annotations }} - annotations: - {{- toYaml .Values.networkManager.pod.annotations | nindent 4 }} - {{- end}} - labels: - {{- include "fluidos.labels" $networkManagerConfig | nindent 4 }} - name: {{ .Values.networkManager.configMaps.nodeIdentity.name }} - namespace: {{ .Release.Namespace }} -data: - {{ if .Values.networkManager.configMaps.nodeIdentity.domain }} - domain: {{ .Values.networkManager.configMaps.nodeIdentity.domain }} - {{- else }} - {{- fail "The FLUIDOS Node domain (.Values.networkManager.configMaps.nodeIdentity.domain) must be set." }} - {{- end }} - {{- if .Values.networkManager.configMaps.nodeIdentity.ip }} - ip: {{ .Values.networkManager.configMaps.nodeIdentity.ip }} - {{- end }} - {{- if .Values.networkManager.configMaps.nodeIdentity.nodeID }} - nodeID: {{ .Values.networkManager.configMaps.nodeIdentity.nodeID }} - {{- else }} - nodeID: {{ randAlphaNum 10 | lower }} - {{- end }} - - diff --git a/deployments/node/templates/fluidos-network-manager-daemonset.yaml b/deployments/node/templates/fluidos-network-manager-daemonset.yaml new file mode 100644 index 00000000..e3d241e3 --- /dev/null +++ b/deployments/node/templates/fluidos-network-manager-daemonset.yaml @@ -0,0 +1,75 @@ +{{- $networkManagerConfig := (merge (dict "name" "network-manager" "module" "network-manager") .) -}} + +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + {{- include "fluidos.labels" $networkManagerConfig | nindent 4 }} + name: {{ include "fluidos.prefixedName" $networkManagerConfig }} + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + {{- include "fluidos.labels" $networkManagerConfig | nindent 6 }} + template: + metadata: + {{- if and .Values.networkManager.pod.annotations (eq .Values.networkManager.config.enableLocalDiscovery true) }} + annotations: + {{- toYaml .Values.networkManager.pod.annotations | nindent 8 }} + {{- end }} + labels: + {{- include "fluidos.labels" $networkManagerConfig | nindent 8 }} + {{- if .Values.networkManager.pod.labels }} + {{ toYaml .Values.networkManager.pod.labels | nindent 8 }} + {{- end }} + spec: + {{- if gt .Values.networkManager.replicas 1.0 }} + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + {{- include "fluidos.labels" $networkManagerConfig | nindent 18 }} + topologyKey: kubernetes.io/hostname + {{- end }} + securityContext: + {{- include "fluidos.podSecurityContext" $networkManagerConfig | nindent 8 }} + serviceAccountName: {{ include "fluidos.prefixedName" $networkManagerConfig }} + containers: + - image: {{ .Values.networkManager.imageName }}:{{ include "fluidos.version" $networkManagerConfig }} + imagePullPolicy: {{ .Values.pullPolicy }} + securityContext: + {{- include "fluidos.containerSecurityContext" $networkManagerConfig | nindent 10 }} + name: {{ $networkManagerConfig.name }} + command: ["/usr/bin/network-manager"] + args: + - --enable-local-discovery={{ .Values.networkManager.config.enableLocalDiscovery | toString }} + {{- if eq .Values.networkManager.config.enableLocalDiscovery true }} + - --cniInterface={{ (get .Values.networkManager.pod.annotations "k8s.v1.cni.cncf.io/networks" | split "@")._1 }} + {{- end }} + env: + - name: MULTICAST_ADDRESS + value: {{ print .Values.networkManager.config.multicast.address ":" .Values.networkManager.config.multicast.port}} + resources: {{- toYaml .Values.networkManager.pod.resources | nindent 10 }} + ports: + - name: healthz + containerPort: 8081 + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: healthz + {{- if ((.Values.common).nodeSelector) }} + nodeSelector: + {{- toYaml .Values.common.nodeSelector | nindent 8 }} + {{- end }} + {{- if ((.Values.common).tolerations) }} + tolerations: + {{- toYaml .Values.common.tolerations | nindent 8 }} + {{- end }} + {{- if ((.Values.common).affinity) }} + affinity: + {{- toYaml .Values.common.affinity | nindent 8 }} + {{- end }} \ No newline at end of file diff --git a/deployments/node/templates/fluidos-network-manager-rbac.yaml b/deployments/node/templates/fluidos-network-manager-rbac.yaml new file mode 100644 index 00000000..8988b35d --- /dev/null +++ b/deployments/node/templates/fluidos-network-manager-rbac.yaml @@ -0,0 +1,32 @@ +{{- $networkManagerConfig := (merge (dict "name" "network-manager" "module" "network-manager") .) -}} + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "fluidos.prefixedName" $networkManagerConfig }} + labels: + {{- include "fluidos.labels" $networkManagerConfig | nindent 4 }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "fluidos.prefixedName" $networkManagerConfig }} + labels: + {{- include "fluidos.labels" $networkManagerConfig | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "fluidos.prefixedName" $networkManagerConfig }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "fluidos.prefixedName" $networkManagerConfig }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "fluidos.prefixedName" $networkManagerConfig }} + labels: + {{- include "fluidos.labels" $networkManagerConfig | nindent 4 }} +{{ .Files.Get (include "fluidos.cluster-role-filename" (dict "prefix" ( include "fluidos.prefixedName" $networkManagerConfig )))}} + diff --git a/deployments/node/templates/fluidos-node-identity-configmap.yaml b/deployments/node/templates/fluidos-node-identity-configmap.yaml new file mode 100644 index 00000000..b789fbee --- /dev/null +++ b/deployments/node/templates/fluidos-node-identity-configmap.yaml @@ -0,0 +1,28 @@ +{{- $nodeIdentityConfig := (merge (dict "name" "node-identity" "module" "node-identity") .) -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "fluidos.labels" $nodeIdentityConfig | nindent 4 }} + name: {{ .Values.common.configMaps.nodeIdentity.name }} + namespace: {{ .Release.Namespace }} +data: + {{ if .Values.common.configMaps.nodeIdentity.domain }} + domain: {{ .Values.common.configMaps.nodeIdentity.domain }} + {{- else }} + {{- fail "The FLUIDOS Node domain (.Values.common.configMaps.nodeIdentity.domain) must be set." }} + {{- end }} + {{- if .Values.common.configMaps.nodeIdentity.nodeID }} + nodeID: {{ .Values.commmon.configMaps.nodeIdentity.nodeID }} + {{- else }} + nodeID: {{ randAlphaNum 10 | lower }} + {{- end }} + {{- if .Values.common.configMaps.nodeIdentity.ip }} + ip: {{ .Values.common.configMaps.nodeIdentity.ip }} + {{- end }} + {{- if .Values.rearController.service.gateway.nodePort.port }} + port: {{ .Values.rearController.service.gateway.nodePort.port | quote }} + {{- else }} + {{- fail "The FLUIDOS Node port (.Values.rearController.service.gateway.nodePort.port) must be set." }} + {{- end }} \ No newline at end of file diff --git a/deployments/node/templates/fluidos-pre-install-hook-network.yaml b/deployments/node/templates/fluidos-pre-install-hook-network.yaml new file mode 100644 index 00000000..1017d874 --- /dev/null +++ b/deployments/node/templates/fluidos-pre-install-hook-network.yaml @@ -0,0 +1,27 @@ +{{- if eq .Values.networkManager.config.enableLocalDiscovery true -}} +{{- $subnet := print .Values.networkManager.config.address.firstOctet "." .Values.networkManager.config.address.secondOctet "." .Values.networkManager.config.address.thirdOctet ".0/24" -}} +{{- $rangeStart := print .Values.networkManager.config.address.firstOctet "." .Values.networkManager.config.address.secondOctet "." .Values.networkManager.config.address.thirdOctet ".101" -}} +{{- $rangeEnd := print .Values.networkManager.config.address.firstOctet "." .Values.networkManager.config.address.secondOctet "." .Values.networkManager.config.address.thirdOctet ".101" -}} + +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: {{ (get .Values.networkManager.pod.annotations "k8s.v1.cni.cncf.io/networks" | split "@")._0 }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-weight": "-6" +spec: + config: '{ + "cniVersion": "0.3.0", + "type": "macvlan", + "master": "eth0", + "mode": "bridge", + "ipam": { + "type": "host-local", + "subnet": "{{ $subnet }}", + "rangeStart": "{{ $rangeStart }}", + "rangeEnd": "{{ $rangeEnd }}" + } + }' +{{- end }} \ No newline at end of file diff --git a/deployments/node/values.yaml b/deployments/node/values.yaml index a82f1093..ab32e530 100644 --- a/deployments/node/values.yaml +++ b/deployments/node/values.yaml @@ -16,6 +16,16 @@ common: affinity: {} # -- Extra arguments for all fluidos-node pods extraArgs: [] + configMaps: + nodeIdentity: + # -- The name of the ConfigMap containing the FLUIDOS Node identity info. + name: "fluidos-node-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" + # -- 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 + nodeID: localResourceManager: # -- The number of REAR Controller, which can be increased for active/passive high availability. @@ -130,25 +140,17 @@ networkManager: requests: {} # -- The resource image to be used by the network-manager pod. 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: "" - # -- The IP List of Remote known FLUIDOS Nodes separated by commas. - remote: - # -- The IP List of SuperNodes separated by commas. - default: - nodeIdentity: - # -- 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: "" - # -- 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 - nodeID: + config: + address: + # -- The first octet of the CNI virtual network subnet + firstOctet: "10" + # -- The second octet of the CNI virtual network subnet + secondOctet: + # -- The third octet of the CNI virtual network subnet + thirdOctet: + multicast: + address: "239.11.11.1" + port: 4000 provider: "your-provider" diff --git a/pkg/local-resource-manager/node_controller.go b/pkg/local-resource-manager/node_controller.go index 9da1406d..679c376a 100644 --- a/pkg/local-resource-manager/node_controller.go +++ b/pkg/local-resource-manager/node_controller.go @@ -39,6 +39,7 @@ import ( // +kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=flavors,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=core,resources=endpoints,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 diff --git a/pkg/local-resource-manager/serviceblueprint_controller.go b/pkg/local-resource-manager/serviceblueprint_controller.go index 36bf7fc3..57110021 100644 --- a/pkg/local-resource-manager/serviceblueprint_controller.go +++ b/pkg/local-resource-manager/serviceblueprint_controller.go @@ -34,6 +34,7 @@ import ( // +kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=flavors,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=core,resources=endpoints,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 // +kubebuilder:rbac:groups=nodecore.fluidos.eu,resources=serviceblueprints,verbs=get;list;watch;create;update;patch;delete diff --git a/pkg/network-manager/doc.go b/pkg/network-manager/doc.go new file mode 100644 index 00000000..8560764f --- /dev/null +++ b/pkg/network-manager/doc.go @@ -0,0 +1,16 @@ +// Copyright 2022-2024 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 networkmanager provides the virtual fabric manager services to manage the peering between FLUIDOS Node. +package networkmanager diff --git a/pkg/network-manager/network-controller.go b/pkg/network-manager/network-controller.go new file mode 100644 index 00000000..ebc6a541 --- /dev/null +++ b/pkg/network-manager/network-controller.go @@ -0,0 +1,269 @@ +// Copyright 2022-2024 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 networkmanager + +import ( + "context" + "encoding/json" + "fmt" + "net" + "os" + "time" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + networkv1alpha1 "github.com/fluidos-project/node/apis/network/v1alpha1" + nodecorev1alpha1 "github.com/fluidos-project/node/apis/nodecore/v1alpha1" + "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" +) + +// clusterRole +// +kubebuilder:rbac:groups=network.fluidos.eu,resources=knownclusters,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=network.fluidos.eu,resources=knownclusters/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch +// +kubebuilder:rbac:groups=core,resources=endpoints,verbs=get;list;watch + +// NetworkManager keeps all the necessary class data. +type NetworkManager struct { + ID *nodecorev1alpha1.NodeIdentity + Multicast string + Iface *net.Interface + EnableLocalDiscovery bool +} + +// KnownClusterReconciler reconciles a KnownCluster object. +type KnownClusterReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// Reconcile reconciles a KnownClusters from DiscoveredClustersList. +func (r *KnownClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := ctrl.LoggerFrom(ctx, "kowncluster", req.NamespacedName) + ctx = ctrl.LoggerInto(ctx, log) + + klog.InfoS("Reconcile triggered", "context", ctx) + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *KnownClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&networkv1alpha1.KnownCluster{}). + Complete(r) +} + +// Setup the Network Manager. +func Setup(ctx context.Context, cl client.Client, nm *NetworkManager, cniInterface *string) error { + klog.Info("Setting up Network Manager routines") + + nodeIdentity := getters.GetNodeIdentity(ctx, cl) + if nodeIdentity == nil { + return fmt.Errorf("failed to get Node Identity") + } + + multicastAddress := os.Getenv("MULTICAST_ADDRESS") + if multicastAddress == "" { + return fmt.Errorf("failed to get multicast address") + } + + nm.ID = nodeIdentity + nm.Multicast = multicastAddress + + if nm.EnableLocalDiscovery { + ifi, err := net.InterfaceByName(*cniInterface) + if err != nil { + return err + } + nm.Iface = ifi + klog.InfoS("Interface", "Name", ifi.Name, "MAC address", ifi.HardwareAddr) + } + + klog.InfoS("Node", "ID", nodeIdentity.NodeID, "Address", nodeIdentity.IP) + + return nil +} + +// Execute the Network Manager routines. +func Execute(ctx context.Context, cl client.Client, nm *NetworkManager) error { + // Start sending multicast messages + if nm.EnableLocalDiscovery { + go func() { + if err := sendMulticastMessage(ctx, nm); err != nil { + klog.ErrorS(err, "Error sending advertisemente") + } + }() + + // Start receiving multicast messages + go func() { + if err := receiveMulticastMessage(ctx, cl, nm); err != nil { + klog.ErrorS(err, "Error receiving advertisement") + } + }() + } + + // Do housekeeping + go func() { + if err := doHousekeeping(ctx, cl); err != nil { + klog.ErrorS(err, "Error doing housekeeping") + } + }() + + return nil +} + +func sendMulticastMessage(ctx context.Context, nm *NetworkManager) error { + message, err := json.Marshal(nm.ID) + if err != nil { + return err + } + + laddr, err := nm.Iface.Addrs() + if err != nil { + return err + } + + dialer := &net.Dialer{ + LocalAddr: &net.UDPAddr{ + IP: laddr[0].(*net.IPNet).IP, + Port: 0, + }, + } + + conn, err := dialer.Dial("udp", nm.Multicast) + if err != nil { + return err + } + defer conn.Close() + + ticker := time.NewTicker(5 * time.Second) + for { + select { + case <-ticker.C: + _, err = conn.Write(message) + if err != nil { + return err + } + klog.Info("Advertisement multicasted") + case <-ctx.Done(): + ticker.Stop() + return nil + } + } +} + +func receiveMulticastMessage(ctx context.Context, cl client.Client, local *NetworkManager) error { + addr, err := net.ResolveUDPAddr("udp", local.Multicast) + if err != nil { + return err + } + + conn, err := net.ListenMulticastUDP("udp", local.Iface, addr) + if err != nil { + return err + } + defer conn.Close() + + buffer := make([]byte, 1024) + + for { + n, _, err := conn.ReadFromUDP(buffer) + if err != nil { + return err + } + + var remote NetworkManager + + err = json.Unmarshal(buffer[:n], &remote.ID) + if err != nil { + klog.Error("Error unmarshalling message: ", err) + continue + } + + // Check if received advertisement is remote + if local.ID.IP != remote.ID.IP { + klog.InfoS("Received remote advertisement", "ID", remote.ID.NodeID, "Address", remote.ID.IP) + + // Fetch the KnownCluster instance if already present + kc := &networkv1alpha1.KnownCluster{} + + if err := cl.Get(ctx, client.ObjectKey{Name: namings.ForgeKnownClusterName(remote.ID.NodeID), Namespace: flags.FluidosNamespace}, kc); err != nil { + if client.IgnoreNotFound(err) == nil { + klog.Info("KnownCluster not found: creating") + + // Create new KnownCluster CR + if err := cl.Create(ctx, resourceforge.ForgeKnownCluster(remote.ID.NodeID, remote.ID.IP)); err != nil { + return err + } + klog.InfoS("KnownCluster created", "ID", remote.ID.NodeID) + } + } else { + klog.Info("KnownCluster already present: updating") + // Update Status + kc.UpdateStatus() + + // Update fetched KnownCluster CR + err := cl.Status().Update(ctx, kc) + if err != nil { + return err + } + klog.InfoS("KnownCluster updated", "ID", kc.ObjectMeta.Name) + } + } + } +} + +func doHousekeeping(ctx context.Context, cl client.Client) error { + ticker := time.NewTicker(20 * time.Second) + for { + select { + case <-ticker.C: + klog.Info("Starting housekeeping") + + // Retrieve KnownClusterList + kcList := networkv1alpha1.KnownClusterList{} + err := cl.List(ctx, &kcList) + if err != nil { + return err + } + if len(kcList.Items) == 0 { + klog.Info("Housekeeping not needed, no KnownClusters available") + continue + } + + // Remove all KnownCluster CR with expiration time < now + for i := range kcList.Items { + kc := &kcList.Items[i] + if tools.CheckExpiration(kc.Status.ExpirationTime) { + err := cl.Delete(ctx, kc) + klog.InfoS("KnownCluster expired and deleted", "ID", kc.Name) + if err != nil { + return err + } + } + } + case <-ctx.Done(): + ticker.Stop() + return nil + } + } +} diff --git a/pkg/rear-controller/discovery-manager/discovery_controller.go b/pkg/rear-controller/discovery-manager/discovery_controller.go index d8df9389..1a04f38e 100644 --- a/pkg/rear-controller/discovery-manager/discovery_controller.go +++ b/pkg/rear-controller/discovery-manager/discovery_controller.go @@ -43,6 +43,7 @@ type DiscoveryReconciler struct { //+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=network.fluidos.eu,resources=knownclusters,verbs=get;list;watch //+kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch // Reconcile is part of the main kubernetes reconciliation loop which aims to diff --git a/pkg/rear-controller/gateway/provider.go b/pkg/rear-controller/gateway/provider.go index 6c32ee44..c9745890 100644 --- a/pkg/rear-controller/gateway/provider.go +++ b/pkg/rear-controller/gateway/provider.go @@ -387,7 +387,7 @@ func (g *Gateway) reserveFlavor(w http.ResponseWriter, r *http.Request) { // Check if the Transaction already exists t, found := g.SearchTransaction(request.Buyer.NodeID, flavorID) if found { - t.ExpirationTime = tools.GetExpirationTime() + t.ExpirationTime = tools.GetExpirationTime(1, 0, 0) transaction = t g.addNewTransaction(t) } diff --git a/pkg/utils/consts/consts.go b/pkg/utils/consts/consts.go index 32f140ac..cc4a84bc 100644 --- a/pkg/utils/consts/consts.go +++ b/pkg/utils/consts/consts.go @@ -15,8 +15,7 @@ package consts const ( - NetworkConfigMapName = "fluidos-network-manager-config" - NodeIdentityConfigMapName = "fluidos-network-manager-identity" + NodeIdentityConfigMapName = "fluidos-node-identity" LiqoClusterIdConfigMapName = "liqo-clusterid-configmap" LiqoNamespace = "liqo" LiqoAuthTokenSecretNamePrefix = "remote-token-" diff --git a/pkg/utils/getters/getters.go b/pkg/utils/getters/getters.go index 9942ed5a..2dbd8786 100644 --- a/pkg/utils/getters/getters.go +++ b/pkg/utils/getters/getters.go @@ -17,16 +17,17 @@ package getters import ( "context" "fmt" - "strings" "github.com/liqotech/liqo/pkg/auth" "github.com/liqotech/liqo/pkg/utils" foreigncluster "github.com/liqotech/liqo/pkg/utils/foreignCluster" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" + networkv1alpha1 "github.com/fluidos-project/node/apis/network/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/consts" @@ -47,27 +48,51 @@ func GetNodeIdentity(ctx context.Context, cl client.Client) *nodecorev1alpha1.No return nil } + // Get the control plane IP address if not available in the ConfigMap + if cm.Data["ip"] == "" { + ep := &corev1.Endpoints{} + + // Get the "kubernetes" endpoint + err := cl.Get(ctx, types.NamespacedName{ + Name: "kubernetes", + Namespace: metav1.NamespaceDefault, + }, ep) + if err != nil { + klog.Errorf("Error getting the endpoint: %s", err) + return nil + } + + cm.Data["ip"] = ep.Subsets[0].Addresses[0].IP + } + return &nodecorev1alpha1.NodeIdentity{ NodeID: cm.Data["nodeID"], Domain: cm.Data["domain"], - IP: cm.Data["ip"], + IP: cm.Data["ip"] + ":" + cm.Data["port"], } } -// GetLocalProviders retrieves the list of local providers ip addresses from the Network Manager configMap. +// GetLocalProviders retrieves the list of local providers ip addresses from the KnownCluster CRs. func GetLocalProviders(ctx context.Context, cl client.Client) []string { - cm := &corev1.ConfigMap{} + knownclusters := networkv1alpha1.KnownClusterList{} + result := []string{} - // Get the configmap - err := cl.Get(ctx, types.NamespacedName{ - Name: consts.NetworkConfigMapName, - Namespace: flags.FluidosNamespace, - }, cm) - if err != nil { - klog.Errorf("Error getting the configmap: %s", err) + // Get the list of KnownClusters + if err := cl.List(ctx, &knownclusters); err != nil { + klog.Errorf("Error when listing KnownClusters: %s", err) + return nil + } + + if len(knownclusters.Items) == 0 { + klog.Infof("No KnownCluster found") return nil } - return strings.Split(cm.Data["local"], ",") + + for i := range knownclusters.Items { + result = append(result, knownclusters.Items[i].Spec.Address) + } + + return result } // GetLiqoCredentials retrieves the Liqo credentials from the local cluster. diff --git a/pkg/utils/namings/namings.go b/pkg/utils/namings/namings.go index edb29eac..7f74cc54 100644 --- a/pkg/utils/namings/namings.go +++ b/pkg/utils/namings/namings.go @@ -124,6 +124,11 @@ func RetrieveFlavorNameFromPC(pcName string) string { return strings.TrimPrefix(pcName, "peeringcandidate-") } +// ForgeKnownClusterName generates a name for the Cluster. +func ForgeKnownClusterName(nodeID string) string { + return fmt.Sprintf("knowncluster-%s", nodeID) +} + // ForgeRandomString generates a random string of 16 bytes. func ForgeRandomString() (string, error) { randomBytes := make([]byte, 16) diff --git a/pkg/utils/resourceforge/forge.go b/pkg/utils/resourceforge/forge.go index aff078b7..5be7fc84 100644 --- a/pkg/utils/resourceforge/forge.go +++ b/pkg/utils/resourceforge/forge.go @@ -33,6 +33,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" advertisementv1alpha1 "github.com/fluidos-project/node/apis/advertisement/v1alpha1" + networkv1alpha1 "github.com/fluidos-project/node/apis/network/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/consts" @@ -413,7 +414,7 @@ func ForgeTransactionObj(id string, req *models.ReserveRequest) *models.Transact } return nil }(), - ExpirationTime: tools.GetExpirationTime(), + ExpirationTime: tools.GetExpirationTime(1, 0, 0), } } @@ -1473,3 +1474,20 @@ func ForgeSecretForService(contract *reservationv1alpha1.Contract, return secretCredentials, nil } + +// ForgeKnownCluster creates a KnownCluster from cluster ID and IP address. +func ForgeKnownCluster(id, address string) *networkv1alpha1.KnownCluster { + return &networkv1alpha1.KnownCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: namings.ForgeKnownClusterName(id), + Namespace: flags.FluidosNamespace, + }, + Spec: networkv1alpha1.KnownClusterSpec{ + Address: address, + }, + Status: networkv1alpha1.KnownClusterStatus{ + ExpirationTime: tools.GetExpirationTime(0, 0, 10), + LastUpdateTime: tools.GetTimeNow(), + }, + } +} diff --git a/pkg/utils/tools/tools.go b/pkg/utils/tools/tools.go index 121503a4..cbec3b0c 100644 --- a/pkg/utils/tools/tools.go +++ b/pkg/utils/tools/tools.go @@ -25,9 +25,9 @@ func GetTimeNow() string { return time.Now().Format(time.RFC3339) } -// GetExpirationTime returns the current time plus 24 hours in RFC3339 format. -func GetExpirationTime() string { - return time.Now().Add(time.Hour * 24).Format(time.RFC3339) +// GetExpirationTime returns the current time plus h hours, m minutes and s seconds in RFC3339 format. +func GetExpirationTime(h, m, s int) string { + return time.Now().Add(time.Hour * time.Duration(h)).Add(time.Minute * time.Duration(m)).Add(time.Second * time.Duration(s)).Format(time.RFC3339) } // CheckExpiration checks if the expirationTimestamp has already expired. diff --git a/quickstart/utils/consumer-values-no-ad.yaml b/quickstart/utils/consumer-values-no-ad.yaml index 1a590cb0..bbb62840 100644 --- a/quickstart/utils/consumer-values-no-ad.yaml +++ b/quickstart/utils/consumer-values-no-ad.yaml @@ -18,6 +18,17 @@ common: affinity: {} # -- Extra arguments for all fluidos-node pods extraArgs: [] + configMaps: + nodeIdentity: + # -- The name of the ConfigMap containing the FLUIDOS Node identity info. + name: "fluidos-node-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" + # -- 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 + nodeID: + localResourceManager: # -- The number of REAR Controller, which can be increased for active/passive high availability. @@ -121,7 +132,9 @@ networkManager: replicas: 1 pod: # -- Annotations for the network-manager pod. - annotations: {} + annotations: { + k8s.v1.cni.cncf.io/networks: macvlan-conf@eth1 + } # -- Labels for the network-manager pod. labels: {} # -- Extra arguments for the network-manager pod. @@ -132,25 +145,18 @@ networkManager: requests: {} # -- The resource image to be used by the network-manager pod. imageName: "ghcr.io/fluidos/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: - # -- The IP List of Remote known FLUIDOS Nodes separated by commas. - remote: - # -- The IP List of SuperNodes separated by commas. - default: - nodeIdentity: - # -- 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" - # -- 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 - nodeID: + config: + enableLocalDiscovery: + address: + # -- The first octet of the CNI virtual network subnet + firstOctet: "10" + # -- The second octet of the CNI virtual network subnet + secondOctet: "101" + # -- The third octet of the CNI virtual network subnet + thirdOctet: + multicast: + address: "239.11.11.1" + port: 4000 webhook: # -- Enable the webhook server for the local-resource-manager. diff --git a/quickstart/utils/consumer-values.yaml b/quickstart/utils/consumer-values.yaml index dcff52e3..e8cc9727 100644 --- a/quickstart/utils/consumer-values.yaml +++ b/quickstart/utils/consumer-values.yaml @@ -18,6 +18,16 @@ common: affinity: {} # -- Extra arguments for all fluidos-node pods extraArgs: [] + configMaps: + nodeIdentity: + # -- The name of the ConfigMap containing the FLUIDOS Node identity info. + name: "fluidos-node-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" + # -- 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 + nodeID: localResourceManager: # -- The number of REAR Controller, which can be increased for active/passive high availability. @@ -121,7 +131,9 @@ networkManager: replicas: 1 pod: # -- Annotations for the network-manager pod. - annotations: {} + annotations: { + k8s.v1.cni.cncf.io/networks: macvlan-conf@eth1 + } # -- Labels for the network-manager pod. labels: {} # -- Extra arguments for the network-manager pod. @@ -132,25 +144,18 @@ networkManager: requests: {} # -- The resource image to be used by the network-manager pod. imageName: "ghcr.io/fluidos/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: - # -- The IP List of Remote known FLUIDOS Nodes separated by commas. - remote: - # -- The IP List of SuperNodes separated by commas. - default: - nodeIdentity: - # -- 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" - # -- 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 - nodeID: + config: + enableLocalDiscovery: + address: + # -- The first octet of the CNI virtual network subnet + firstOctet: "10" + # -- The second octet of the CNI virtual network subnet + secondOctet: "101" + # -- The third octet of the CNI virtual network subnet + thirdOctet: + multicast: + address: "239.11.11.1" + port: 4000 webhook: # -- Enable the webhook server for the local-resource-manager. diff --git a/quickstart/utils/provider-values-no-ad.yaml b/quickstart/utils/provider-values-no-ad.yaml index b9a55d63..dd6b04c0 100644 --- a/quickstart/utils/provider-values-no-ad.yaml +++ b/quickstart/utils/provider-values-no-ad.yaml @@ -18,6 +18,16 @@ common: affinity: {} # -- Extra arguments for all fluidos-node pods extraArgs: [] + configMaps: + nodeIdentity: + # -- The name of the ConfigMap containing the FLUIDOS Node identity info. + name: "fluidos-node-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" + # -- 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 + nodeID: localResourceManager: # -- The number of REAR Controller, which can be increased for active/passive high availability. @@ -121,7 +131,9 @@ networkManager: replicas: 1 pod: # -- Annotations for the network-manager pod. - annotations: {} + annotations: { + k8s.v1.cni.cncf.io/networks: macvlan-conf@eth1 + } # -- Labels for the network-manager pod. labels: {} # -- Extra arguments for the network-manager pod. @@ -132,26 +144,18 @@ networkManager: requests: {} # -- The resource image to be used by the network-manager pod. imageName: "ghcr.io/fluidos/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: "host.docker.internal:9000" - # -- The IP List of Remote known FLUIDOS Nodes separated by commas. - remote: - # -- The IP List of SuperNodes separated by commas. - default: - nodeIdentity: - # -- 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" - # -- 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 - nodeID: - + config: + enableLocalDiscovery: + address: + # -- The first octet of the CNI virtual network subnet + firstOctet: "10" + # -- The second octet of the CNI virtual network subnet + secondOctet: "202" + # -- The third octet of the CNI virtual network subnet + thirdOctet: + multicast: + address: "239.11.11.1" + port: 4000 webhook: # -- Enable the webhook server for the local-resource-manager. diff --git a/quickstart/utils/provider-values.yaml b/quickstart/utils/provider-values.yaml index 1152042d..ff660679 100644 --- a/quickstart/utils/provider-values.yaml +++ b/quickstart/utils/provider-values.yaml @@ -18,6 +18,16 @@ common: affinity: {} # -- Extra arguments for all fluidos-node pods extraArgs: [] + configMaps: + nodeIdentity: + # -- The name of the ConfigMap containing the FLUIDOS Node identity info. + name: "fluidos-node-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" + # -- 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 + nodeID: localResourceManager: # -- The number of REAR Controller, which can be increased for active/passive high availability. @@ -121,7 +131,9 @@ networkManager: replicas: 1 pod: # -- Annotations for the network-manager pod. - annotations: {} + annotations: { + k8s.v1.cni.cncf.io/networks: macvlan-conf@eth1 + } # -- Labels for the network-manager pod. labels: {} # -- Extra arguments for the network-manager pod. @@ -132,25 +144,18 @@ networkManager: requests: {} # -- The resource image to be used by the network-manager pod. imageName: "ghcr.io/fluidos/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: "host.docker.internal:9000" - # -- The IP List of Remote known FLUIDOS Nodes separated by commas. - remote: - # -- The IP List of SuperNodes separated by commas. - default: - nodeIdentity: - # -- 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" - # -- 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 - nodeID: + config: + enableLocalDiscovery: + address: + # -- The first octet of the CNI virtual network subnet + firstOctet: "10" + # -- The second octet of the CNI virtual network subnet + secondOctet: "202" + # -- The third octet of the CNI virtual network subnet + thirdOctet: + multicast: + address: "239.11.11.1" + port: 4000 webhook: # -- Enable the webhook server for the local-resource-manager. diff --git a/testbed/kind/provider/values.yaml b/testbed/kind/provider/values.yaml index 37346f96..a8c67943 100644 --- a/testbed/kind/provider/values.yaml +++ b/testbed/kind/provider/values.yaml @@ -135,7 +135,7 @@ networkManager: # -- 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: "host.docker.internal:9000" + local: "" # -- The IP List of Remote known FLUIDOS Nodes separated by commas. remote: # -- The IP List of SuperNodes separated by commas. diff --git a/testbed/kind/setup.sh b/testbed/kind/setup.sh old mode 100644 new mode 100755 diff --git a/tools/scripts/clean-dev-env.sh b/tools/scripts/clean-dev-env.sh old mode 100644 new mode 100755 index 9ea58eed..a3fa4d8a --- a/tools/scripts/clean-dev-env.sh +++ b/tools/scripts/clean-dev-env.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/bash # Function to delete a kind cluster and its config file delete_kind_cluster_and_config() { diff --git a/tools/scripts/environment.sh b/tools/scripts/environment.sh index 899b45f7..24808ff5 100644 --- a/tools/scripts/environment.sh +++ b/tools/scripts/environment.sh @@ -72,6 +72,19 @@ create_kind_clusters() { role="consumer" # Create the cluster kind create cluster --name "$name" --config "$SCRIPT_DIR"/../../quickstart/kind/configs/standard.yaml --kubeconfig "$SCRIPT_DIR"/"$name"-config -q + # Install macvlan plugin to enable multicast node discovery, if required + if [ "$6" == "true" ]; then + num_workers=$(kind get nodes --name fluidos-consumer-1 | grep worker -c) + for j in $(seq 1 "$num_workers"); do + ( + docker exec --workdir /tmp "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" mkdir -p cni-plugins + docker exec --workdir /tmp/cni-plugins "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" curl -LO https://github.com/containernetworking/plugins/releases/download/v1.5.1/cni-plugins-linux-amd64-v1.5.1.tgz + docker exec --workdir /tmp/cni-plugins "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" tar xvfz cni-plugins-linux-amd64-v1.5.1.tgz + docker exec --workdir /tmp/cni-plugins "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" cp macvlan /opt/cni/bin + docker exec --workdir /tmp "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" rm -r cni-plugins + ) + done + fi # Get the IP of the control plane of the cluster controlplane_ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$name"-control-plane) # Write the cluster info to a file @@ -91,6 +104,19 @@ create_kind_clusters() { role="provider" # Create the cluster kind create cluster --name "$name" --config "$SCRIPT_DIR"/../../quickstart/kind/configs/standard.yaml --kubeconfig "$SCRIPT_DIR"/"$name"-config -q + # Install macvlan plugin to enable multicast node discovery, if required + if [ "$6" == "true" ]; then + num_workers=$(kind get nodes --name fluidos-provider-1 | grep worker -c) + for j in $(seq 1 "$num_workers"); do + ( + docker exec --workdir /tmp "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" mkdir -p cni-plugins + docker exec --workdir /tmp/cni-plugins "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" curl -LO https://github.com/containernetworking/plugins/releases/download/v1.5.1/cni-plugins-linux-amd64-v1.5.1.tgz + docker exec --workdir /tmp/cni-plugins "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" tar xvfz cni-plugins-linux-amd64-v1.5.1.tgz + docker exec --workdir /tmp/cni-plugins "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" cp macvlan /opt/cni/bin + docker exec --workdir /tmp "$name"-worker"$([ "$j" = 1 ] && echo "" || echo "$j")" rm -r cni-plugins + ) + done + fi # Get the IP of the control plane of the cluster controlplane_ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$name"-control-plane) # Write the cluster info to a file diff --git a/tools/scripts/install_liqo.sh b/tools/scripts/install_liqo.sh index 842d94db..6d334b21 100644 --- a/tools/scripts/install_liqo.sh +++ b/tools/scripts/install_liqo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/bash # Function to check if liqoctl is installed diff --git a/tools/scripts/installation.sh b/tools/scripts/installation.sh index d7391b40..ee3fb47a 100644 --- a/tools/scripts/installation.sh +++ b/tools/scripts/installation.sh @@ -6,8 +6,6 @@ SCRIPT_DIR="$(dirname "$SCRIPT_PATH")" # shellcheck disable=SC1091 source "$SCRIPT_DIR"/utils.sh -declare -A providers_ips - # PIDs of the processes in background pids=() @@ -57,15 +55,14 @@ build_and_load() { # $2: provider JSON tmp file # $3: local repositories boolean # $4: local resource manager boolean +# $5: kubernetes clusters type +# $6: network manager installation boolean # Return: none function install_components() { unset clusters declare -A clusters - unset providers_ips - declare -A providers_ips - # Get consumer JSON tmp file from parameter consumers_json=$1 @@ -81,10 +78,10 @@ function install_components() { # Get the kubernetes clusters type from parameters installation_type=$5 - helm repo add fluidos https://fluidos-project.github.io/node/ + # Get the network manager installation boolean from parameters + enable_local_discovery=$6 - consumer_node_port=30000 - provider_node_port=30001 + helm repo add fluidos https://fluidos-project.github.io/node/ # Read the results from the files while IFS= read -r line; do @@ -112,11 +109,12 @@ function install_components() { COMPONENT_MAP["rear-controller"]="rearController.imageName" COMPONENT_MAP["rear-manager"]="rearManager.imageName" COMPONENT_MAP["local-resource-manager"]="localResourceManager.imageName" + COMPONENT_MAP["network-manager"]="networkManager.imageName" # Build the image name using the username IMAGE_SET_STRING="" DOCKER_USERNAME="fluidoscustom" VERSION="0.0.1" - for component in rear-controller rear-manager local-resource-manager; do + for component in rear-controller rear-manager local-resource-manager network-manager; do helm_key="${COMPONENT_MAP[$component]}" IMAGE_SET_STRING="$IMAGE_SET_STRING --set $helm_key=$DOCKER_USERNAME/$component" # Build and load the docker image @@ -144,36 +142,15 @@ function install_components() { echo "Cluster is: $cluster" echo "Cluster value is: ${clusters[$cluster]}" - # Create list of providers ip taking all the clusters controlplane IPs from the map and put it ina string separated by commas - for provider in "${!clusters[@]}"; do - # Check if the cluster is not the current one - # Check if the cluster is a provider - cluster_role=$(jq -r '.role' <<< "${clusters[$provider]}") - # Print cluster role - echo "Cluster role is: $cluster_role" - if [ "$provider" != "$cluster" ] && [ "$cluster_role" == "provider" ]; then - # Print the specific cluster informations - echo "Provider cluster: $provider" - echo "Value: ${clusters[$provider]}" - ip_value="${clusters[$provider]}" - ip=$(jq -r '.ip' <<< "$ip_value") - # Add the provider port to the IP - ip="$ip:$provider_node_port" - - if [ -z "${providers_ips[$cluster]}" ]; then - providers_ips[$cluster]="$ip" - else - providers_ips[$cluster]="${providers_ips[$cluster]}\,$ip" - fi - fi - done - - echo "Providers IPs for cluster $cluster: ${providers_ips[$cluster]}" - # Get the kubeconfig file which depends on variable installation_type KUBECONFIG=$(jq -r '.kubeconfig' <<< "${clusters[$cluster]}") echo "The KUBECONFIG is $KUBECONFIG" + + # Setup CNI to enable multicast node discovery + if [ "$enable_local_discovery" == "true" ]; then + kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml --kubeconfig "$KUBECONFIG" + fi # Decide value file to use based on the role of the cluster if [ "$(jq -r '.role' <<< "${clusters[$cluster]}")" == "consumer" ]; then @@ -183,10 +160,9 @@ function install_components() { else value_file="$SCRIPT_DIR/../../quickstart/utils/consumer-values-no-ad.yaml" fi - # Get cluster IP and port + # Get control plane IP ip_value="${clusters[$cluster]}" ip=$(jq -r '.ip' <<< "$ip_value") - port=$consumer_node_port else # Skip this installation if the cluster is a provider and its installation type is not kind if [ "$installation_type" != "kind" ]; then @@ -199,10 +175,9 @@ function install_components() { else value_file="$SCRIPT_DIR/../../quickstart/utils/provider-values-no-ad.yaml" fi - # Get cluster IP and port + # Get control plane IP ip_value="${clusters[$cluster]}" ip=$(jq -r '.ip' <<< "$ip_value") - port=$provider_node_port fi fi @@ -230,16 +205,18 @@ function install_components() { -n fluidos --create-namespace -f $value_file $IMAGE_SET_STRING \ --set tag=$VERSION \ --set "provider=$installation_type" \ - --set "networkManager.configMaps.nodeIdentity.ip=$ip:$port" \ - --set "networkManager.configMaps.providers.local=${providers_ips[$cluster]}" \ + --set "common.configMaps.nodeIdentity.ip=$ip" \ + --set "networkManager.config.enableLocalDiscovery=$enable_local_discovery" \ + --set "networkManager.config.address.thirdOctet=${cluster: -1}" \ --wait \ --kubeconfig $KUBECONFIG else echo "Installing remote repositories in cluster $cluster with local resource manager" helm upgrade --install node fluidos/node -n fluidos --create-namespace -f "$value_file" \ --set "provider=$installation_type" \ - --set "networkManager.configMaps.nodeIdentity.ip=$ip:$port" \ - --set 'networkManager.configMaps.providers.local'="${providers_ips[$cluster]}" \ + --set "common.configMaps.nodeIdentity.ip=$ip" \ + --set "networkManager.config.enableLocalDiscovery=$enable_local_discovery" \ + --set "networkManager.config.address.thirdOctet=${cluster: -1}" \ --wait \ --kubeconfig "$KUBECONFIG" fi diff --git a/tools/scripts/setup.sh b/tools/scripts/setup.sh old mode 100644 new mode 100755 index 01f14032..2ec6043e --- a/tools/scripts/setup.sh +++ b/tools/scripts/setup.sh @@ -84,6 +84,19 @@ else return 1 fi +# Ask the user if they want to use the node discovery from the network resource manager +read -r -p "Do you want to enable LAN node discovery? [y/n] " enable_local_discovery + +# Check if the input is y or n +if [ "$enable_local_discovery" == "y" ]; then + enable_local_discovery=true +elif [ "$enable_local_discovery" == "n" ]; then + enable_local_discovery=false +else + echo "Invalid option." + return 1 +fi + # Check requirements with function check_tools from requirements.sh check_tools @@ -94,7 +107,7 @@ if [ "$environment_type" -eq 1 ]; then environment_type="customkind" installation_type="kind" # Call create_kind clusters with parameters and save return value into clusters variable - create_kind_clusters "$consumers_json" "$providers_json" $environment_type 1 1 + create_kind_clusters "$consumers_json" "$providers_json" $environment_type 1 1 $enable_local_discovery elif [ "$environment_type" -eq 2 ]; then environment_type="customkind" installation_type="kind" @@ -109,7 +122,7 @@ elif [ "$environment_type" -eq 2 ]; then fi # Call create_kind clusters with parameters and save return value into clusters variable - create_kind_clusters "$consumers_json" "$providers_json" $environment_type "$consumer_clusters" "$provider_clusters" + create_kind_clusters "$consumers_json" "$providers_json" $environment_type "$consumer_clusters" "$provider_clusters" $enable_local_discovery # elif [ "$environment_type" -eq 3 ]; then # # Ask the user what Kubernetes clusters they want to use between kubeadm and k3s # read -r -p "What type of Kubernetes clusters do you want to use? @@ -132,7 +145,7 @@ else fi # FLUIDOS node installation -install_components "$consumers_json" "$providers_json" $local_repositories $enable_auto_discovery $installation_type +install_components "$consumers_json" "$providers_json" $local_repositories $enable_auto_discovery $installation_type $enable_local_discovery print_title "Installation completed successfully"