diff --git a/api/v1alpha1/ipfscluster_types.go b/api/v1alpha1/ipfscluster_types.go index 1fdbc84a..7403c417 100644 --- a/api/v1alpha1/ipfscluster_types.go +++ b/api/v1alpha1/ipfscluster_types.go @@ -35,13 +35,18 @@ const ( type ReproviderStrategy string +type ExternalStrategy string + const ( // ReproviderStrategyAll Announces the CID of every stored block. ReproviderStrategyAll ReproviderStrategy = "all" // ReproviderStrategyPinned Only announces the pinned CIDs recursively. ReproviderStrategyPinned ReproviderStrategy = "pinned" // ReproviderStrategyRoots Only announces the root block of explicitly pinned CIDs. - ReproviderStrategyRoots ReproviderStrategy = "roots" + ReproviderStrategyRoots ReproviderStrategy = "roots" + ExternalStrategyNone ExternalStrategy = "None" + ExternalStrategyIngress ExternalStrategy = "Ingress" + ExternalStrategyLoadBalancer ExternalStrategy = "LoadBalancer" ) type ReprovideSettings struct { @@ -55,6 +60,14 @@ type ReprovideSettings struct { Interval string `json:"interval,omitempty"` } +type ExternalSettings struct { + // +kubebuilder:validation:Enum={Ingress,LoadBalancer,None} + // +optional + Strategy ExternalStrategy `json:"strategy,omitempty"` + // +optional + AppendAnnotations map[string]string `json:"appendAnnotations,omitempty"` +} + type followParams struct { Name string `json:"name"` Template string `json:"template"` @@ -69,6 +82,7 @@ type networkConfig struct { type IpfsClusterSpec struct { // url defines the URL to be using as an ingress controller. // +kubebuilder:validation:Optional + // Reprovider Describes the settings that each IPFS node URL string `json:"url"` // public determines whether or not we should be exposing this IPFS Cluster to the public. Public bool `json:"public"` @@ -91,6 +105,8 @@ type IpfsClusterSpec struct { // should use when reproviding content. // +optional Reprovider ReprovideSettings `json:"reprovider,omitempty"` + Gateway *ExternalSettings `json:"gateway,omitempty"` + ClusterAPI *ExternalSettings `json:"clusterApi,omitempty"` } type IpfsClusterStatus struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 50c45caa..89e35d78 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -117,6 +117,28 @@ func (in *CircuitRelayStatus) DeepCopy() *CircuitRelayStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExternalSettings) DeepCopyInto(out *ExternalSettings) { + *out = *in + if in.AppendAnnotations != nil { + in, out := &in.AppendAnnotations, &out.AppendAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalSettings. +func (in *ExternalSettings) DeepCopy() *ExternalSettings { + if in == nil { + return nil + } + out := new(ExternalSettings) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IpfsCluster) DeepCopyInto(out *IpfsCluster) { *out = *in @@ -193,6 +215,16 @@ func (in *IpfsClusterSpec) DeepCopyInto(out *IpfsClusterSpec) { (*in).DeepCopyInto(*out) } out.Reprovider = in.Reprovider + if in.Gateway != nil { + in, out := &in.Gateway, &out.Gateway + *out = new(ExternalSettings) + (*in).DeepCopyInto(*out) + } + if in.ClusterAPI != nil { + in, out := &in.ClusterAPI, &out.ClusterAPI + *out = new(ExternalSettings) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IpfsClusterSpec. diff --git a/config/crd/bases/cluster.ipfs.io_ipfsclusters.yaml b/config/crd/bases/cluster.ipfs.io_ipfsclusters.yaml index 0262f9aa..34f29d00 100644 --- a/config/crd/bases/cluster.ipfs.io_ipfsclusters.yaml +++ b/config/crd/bases/cluster.ipfs.io_ipfsclusters.yaml @@ -35,6 +35,19 @@ spec: spec: description: IpfsClusterSpec defines the desired state of the IpfsCluster. properties: + clusterApi: + properties: + appendAnnotations: + additionalProperties: + type: string + type: object + strategy: + enum: + - Ingress + - LoadBalancer + - None + type: string + type: object clusterStorage: anyOf: - type: integer @@ -57,6 +70,19 @@ spec: - template type: object type: array + gateway: + properties: + appendAnnotations: + additionalProperties: + type: string + type: object + strategy: + enum: + - Ingress + - LoadBalancer + - None + type: string + type: object ipfsResources: description: ipfsResources specifies the resource requirements for each IPFS container. If this value is omitted, then the operator @@ -131,6 +157,7 @@ spec: type: object url: description: url defines the URL to be using as an ingress controller. + Reprovider Describes the settings that each IPFS node type: string required: - clusterStorage diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index ee5a18bc..385b8aaf 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -12,4 +12,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: quay.io/redhat-et-ipfs/ipfs-operator + newName: coryschwartz/ipfs-operator diff --git a/controllers/circuitrelay_controller.go b/controllers/circuitrelay_controller.go index 9cfa6e9b..f161c52d 100644 --- a/controllers/circuitrelay_controller.go +++ b/controllers/circuitrelay_controller.go @@ -214,7 +214,7 @@ func (r *CircuitRelayReconciler) serviceRelay( { Name: "swarm", Protocol: corev1.ProtocolTCP, - Port: portSwarm, + Port: portIpfsSwarm, TargetPort: intstr.FromString("swarm"), }, // Commented out because some load balancers don't support TCP+UDP:( @@ -420,18 +420,18 @@ func (r *CircuitRelayReconciler) deploymentRelay( Ports: []corev1.ContainerPort{ { Name: "swarm", - ContainerPort: portSwarm, + ContainerPort: portIpfsSwarm, Protocol: "TCP", }, { // Should this port number be the same as portSwarm or should it be a different one? Name: "swarm-udp", - ContainerPort: portSwarmUDP, + ContainerPort: portIpfsSwarmUDP, Protocol: "UDP", }, { Name: "pprof", - ContainerPort: portPprof, + ContainerPort: portIpfsPprof, Protocol: "UDP", }, }, diff --git a/controllers/ipfscluster_controller.go b/controllers/ipfscluster_controller.go index 73b0c4fa..1bbf46f8 100644 --- a/controllers/ipfscluster_controller.go +++ b/controllers/ipfscluster_controller.go @@ -136,6 +136,8 @@ func (r *IpfsClusterReconciler) createTrackedObjects( ) map[client.Object]controllerutil.MutateFn { sa := corev1.ServiceAccount{} svc := corev1.Service{} + gwsvc := corev1.Service{} + apisvc := corev1.Service{} cmScripts := corev1.ConfigMap{} secConfig := corev1.Secret{} sts := appsv1.StatefulSet{} @@ -149,8 +151,8 @@ func (r *IpfsClusterReconciler) createTrackedObjects( instance, &secConfig, []byte(clusterSecret), + []byte(peerID.String()), []byte(privateString), - peerID.String(), ) mutSts := r.StatefulSet(instance, &sts, svcName, secConfigName, cmScriptName) @@ -161,6 +163,19 @@ func (r *IpfsClusterReconciler) createTrackedObjects( &secConfig: mutSecConfig, &sts: mutSts, } + + if instance.Spec.Gateway != nil { + mutgwsvc, _ := r.serviceGateway(instance, &gwsvc) + if instance.Spec.Gateway.Strategy == clusterv1alpha1.ExternalStrategyLoadBalancer { + trackedObjects[&gwsvc] = mutgwsvc + } + } + if instance.Spec.ClusterAPI != nil { + mutapisvc, _ := r.serviceAPI(instance, &apisvc) + if instance.Spec.ClusterAPI.Strategy == clusterv1alpha1.ExternalStrategyLoadBalancer { + trackedObjects[&apisvc] = mutapisvc + } + } return trackedObjects } diff --git a/controllers/ipfscluster_controller_test.go b/controllers/ipfscluster_controller_test.go index 030120f4..ddf2fb16 100644 --- a/controllers/ipfscluster_controller_test.go +++ b/controllers/ipfscluster_controller_test.go @@ -85,7 +85,7 @@ var _ = Describe("IPFS Reconciler", func() { }) It("creates a new peer ids", func() { secretConfig := &v1.Secret{} - fn, _ := ipfsReconciler.SecretConfig(ctx, ipfs, secretConfig, clusterSec, bootstrapKey, clusterPeerID) + fn, _ := ipfsReconciler.SecretConfig(ctx, ipfs, secretConfig, clusterSec, bootstrapKey, []byte(clusterPeerID)) Expect(fn()).To(BeNil()) secretStringToData(secretConfig) expectedKeys := int(replicas)*2 + alwaysKeys @@ -93,7 +93,7 @@ var _ = Describe("IPFS Reconciler", func() { // increase the replica count. Expect to see new keys generated. ipfs.Spec.Replicas++ - fn, _ = ipfsReconciler.SecretConfig(ctx, ipfs, secretConfig, clusterSec, bootstrapKey, clusterPeerID) + fn, _ = ipfsReconciler.SecretConfig(ctx, ipfs, secretConfig, clusterSec, bootstrapKey, []byte(clusterPeerID)) Expect(fn()).To(BeNil()) secretStringToData(secretConfig) Expect(len(secretConfig.Data)).To(Equal(expectedKeys + 2)) diff --git a/controllers/secret.go b/controllers/secret.go index f6d23964..4477258e 100644 --- a/controllers/secret.go +++ b/controllers/secret.go @@ -33,8 +33,8 @@ func (r *IpfsClusterReconciler) SecretConfig( m *clusterv1alpha1.IpfsCluster, sec *corev1.Secret, clusterSecret, + bootstrapPeerID, bootstrapPrivateKey []byte, - peerID string, ) (controllerutil.MutateFn, string) { secName := "ipfs-cluster-" + m.Name @@ -60,7 +60,7 @@ func (r *IpfsClusterReconciler) SecretConfig( } expectedSecret.Data["CLUSTER_SECRET"] = clusterSecret expectedSecret.Data["BOOTSTRAP_PEER_PRIV_KEY"] = bootstrapPrivateKey - expectedSecret.StringData["BOOTSTRAP_PEER_ID"] = peerID + expectedSecret.Data["BOOTSTRAP_PEER_ID"] = bootstrapPeerID } else { // secret exists. // test if we need to add more identieis diff --git a/controllers/service.go b/controllers/service.go index be054ae3..3708bccc 100644 --- a/controllers/service.go +++ b/controllers/service.go @@ -10,67 +10,112 @@ import ( clusterv1alpha1 "github.com/redhat-et/ipfs-operator/api/v1alpha1" ) +// This is an internal service. It exposes the API and gateway ports func (r *IpfsClusterReconciler) serviceCluster( m *clusterv1alpha1.IpfsCluster, svc *corev1.Service, ) (controllerutil.MutateFn, string) { - svcName := "ipfs-cluster-" + m.Name - expected := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: svcName, - Namespace: m.Namespace, - // TODO: annotations for external dns - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "swarm", - Protocol: corev1.ProtocolTCP, - Port: portSwarm, - TargetPort: intstr.FromString("swarm"), - }, - { - Name: "swarm-udp", - Protocol: corev1.ProtocolUDP, - Port: portSwarmUDP, - TargetPort: intstr.FromString("swarm-udp"), - }, - { - Name: "ws", - Protocol: corev1.ProtocolTCP, - Port: portWS, - TargetPort: intstr.FromString("ws"), - }, - { - Name: "http", - Protocol: corev1.ProtocolTCP, - Port: portHTTP, - TargetPort: intstr.FromString("http"), - }, - { - Name: "api-http", - Protocol: corev1.ProtocolTCP, - Port: portAPIHTTP, - TargetPort: intstr.FromString("api-http"), - }, - { - Name: "proxy-http", - Protocol: corev1.ProtocolTCP, - Port: portProxyHTTP, - TargetPort: intstr.FromString("proxy-http"), - }, - { - Name: "cluster-swarm", - Protocol: corev1.ProtocolTCP, - Port: portClusterSwarm, - TargetPort: intstr.FromString("cluster-swarm"), - }, + svcName := "ipfs-cluster-internal-" + m.Name + expected := expectedService( + svcName, + m.Name, + m.Namespace, + "ClusterIP", + m.Annotations, + []corev1.ServicePort{ + { + Name: nameIpfsHTTP, + Protocol: corev1.ProtocolTCP, + Port: portIpfsHTTP, + TargetPort: intstr.FromString(nameIpfsHTTP), }, - Selector: map[string]string{ - "app.kubernetes.io/name": "ipfs-cluster-" + m.Name, + { + Name: nameClusterAPI, + Protocol: corev1.ProtocolTCP, + Port: portClusterAPI, + TargetPort: intstr.FromString(nameClusterAPI), + }, + { + Name: nameClusterProxy, + Protocol: corev1.ProtocolTCP, + Port: portClusterProxy, + TargetPort: intstr.FromString(nameClusterProxy), }, }, + ) + + expected.DeepCopyInto(svc) + // FIXME: catch this error before we run the function being returned + if err := ctrl.SetControllerReference(m, svc, r.Scheme); err != nil { + return func() error { return err }, "" + } + return func() error { + svc.Spec = expected.Spec + return nil + }, svcName +} + +// If enabled, IPFS gateway serivce +func (r *IpfsClusterReconciler) serviceGateway( + m *clusterv1alpha1.IpfsCluster, + svc *corev1.Service, +) (controllerutil.MutateFn, string) { + svcName := "ipfs-cluster-gateway-" + m.Name + annotations := map[string]string{} + for k, v := range m.Spec.Gateway.AppendAnnotations { + annotations[k] = v + } + expected := expectedService( + svcName, + m.Name, + m.Namespace, + "LoadBalancer", + annotations, + []corev1.ServicePort{ + { + Name: nameIpfsHTTP, + Protocol: corev1.ProtocolTCP, + Port: portIpfsHTTP, + TargetPort: intstr.FromString(nameIpfsHTTP), + }, + }, + ) + expected.DeepCopyInto(svc) + // FIXME: catch this error before we run the function being returned + if err := ctrl.SetControllerReference(m, svc, r.Scheme); err != nil { + return func() error { return err }, "" + } + return func() error { + svc.Spec = expected.Spec + return nil + }, svcName +} + +// If enabled, the cluster API +func (r *IpfsClusterReconciler) serviceAPI( + m *clusterv1alpha1.IpfsCluster, + svc *corev1.Service, +) (controllerutil.MutateFn, string) { + svcName := "ipfs-cluster-api-" + m.Name + annotations := map[string]string{} + for k, v := range m.Spec.ClusterAPI.AppendAnnotations { + annotations[k] = v } + expected := expectedService( + svcName, + m.Name, + m.Namespace, + "LoadBalancer", + annotations, + []corev1.ServicePort{ + { + Name: nameClusterAPI, + Protocol: corev1.ProtocolTCP, + Port: portClusterAPI, + TargetPort: intstr.FromString(nameClusterAPI), + }, + }, + ) expected.DeepCopyInto(svc) // FIXME: catch this error before we run the function being returned if err := ctrl.SetControllerReference(m, svc, r.Scheme); err != nil { @@ -81,3 +126,26 @@ func (r *IpfsClusterReconciler) serviceCluster( return nil }, svcName } + +func expectedService(svcName, + relName, + namespace string, + svcType corev1.ServiceType, + annotations map[string]string, + ports []corev1.ServicePort) *corev1.Service { + expected := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: svcName, + Namespace: namespace, + Annotations: annotations, + }, + Spec: corev1.ServiceSpec{ + Type: svcType, + Ports: ports, + Selector: map[string]string{ + "app.kubernetes.io/name": "ipfs-cluster-" + relName, + }, + }, + } + return expected +} diff --git a/controllers/statefulset.go b/controllers/statefulset.go index ab2fc803..041a224b 100644 --- a/controllers/statefulset.go +++ b/controllers/statefulset.go @@ -24,15 +24,26 @@ const ( // Defines port numbers to be used by the IPFS containers. const ( - portAPIHTTP = 9094 - portProxyHTTP = 9095 + portClusterAPI = 9094 + portClusterProxy = 9095 portClusterSwarm = 9096 - portSwarm = 4001 - portSwarmUDP = 4002 - portAPI = 5001 - portPprof = 6060 - portWS = 8081 - portHTTP = 8080 + + portIpfsSwarm = 4001 + portIpfsSwarmUDP = 4002 + portIpfsAPI = 5001 + portIpfsPprof = 6060 + portIpfsWS = 8081 + portIpfsHTTP = 8080 + + nameClusterAPI = "cluster-api" + nameClusterProxy = "cluster-proxy" + nameClusterSwarm = "cluster-swarm" + + nameIpfsSwarm = "swarm" + nameIpfsSwarmUDP = "swarm-udp" + nameIpfsAPI = "api" + nameIpfsWS = "ws" + nameIpfsHTTP = "http" ) // Defines common names @@ -135,28 +146,28 @@ func (r *IpfsClusterReconciler) StatefulSet(m *clusterv1alpha1.IpfsCluster, }, Ports: []corev1.ContainerPort{ { - Name: "swarm", - ContainerPort: portSwarm, + Name: nameIpfsSwarm, + ContainerPort: portIpfsSwarm, Protocol: corev1.ProtocolTCP, }, { - Name: "swarm-udp", - ContainerPort: portSwarmUDP, + Name: nameIpfsSwarmUDP, + ContainerPort: portIpfsSwarmUDP, Protocol: corev1.ProtocolUDP, }, { - Name: "api", - ContainerPort: portAPI, + Name: nameIpfsAPI, + ContainerPort: portIpfsAPI, Protocol: corev1.ProtocolTCP, }, { - Name: "ws", - ContainerPort: portWS, + Name: nameIpfsWS, + ContainerPort: portIpfsWS, Protocol: corev1.ProtocolTCP, }, { - Name: "http", - ContainerPort: portHTTP, + Name: nameIpfsHTTP, + ContainerPort: portIpfsHTTP, Protocol: corev1.ProtocolTCP, }, }, @@ -234,17 +245,17 @@ func (r *IpfsClusterReconciler) StatefulSet(m *clusterv1alpha1.IpfsCluster, }, Ports: []corev1.ContainerPort{ { - Name: "api-http", - ContainerPort: portAPIHTTP, + Name: nameClusterAPI, + ContainerPort: portClusterAPI, Protocol: corev1.ProtocolTCP, }, { - Name: "proxy-http", - ContainerPort: portProxyHTTP, - Protocol: corev1.ProtocolUDP, + Name: nameClusterProxy, + ContainerPort: portClusterProxy, + Protocol: corev1.ProtocolTCP, }, { - Name: "cluster-swarm", + Name: nameClusterSwarm, ContainerPort: portClusterSwarm, Protocol: corev1.ProtocolTCP, }, @@ -252,7 +263,7 @@ func (r *IpfsClusterReconciler) StatefulSet(m *clusterv1alpha1.IpfsCluster, LivenessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ - Port: intstr.FromString("cluster-swarm"), + Port: intstr.FromString(nameClusterSwarm), }, }, InitialDelaySeconds: thirtySeconds, diff --git a/examples/collab-follow.yaml b/examples/collab-follow.yaml index e116e5ae..28995c83 100644 --- a/examples/collab-follow.yaml +++ b/examples/collab-follow.yaml @@ -7,6 +7,10 @@ spec: url: apps.jephilli-4-11-04-28-0655.devcluster.openshift.com ipfsStorage: 5Ti clusterStorage: 20Gi + clusterApi: + strategy: loadbalancer + gateway: + strategy: loadbalancer public: true replicas: 5 follows: diff --git a/examples/ipfs-small.yaml b/examples/ipfs-small.yaml index ea066e8f..72cb9562 100644 --- a/examples/ipfs-small.yaml +++ b/examples/ipfs-small.yaml @@ -8,6 +8,10 @@ metadata: spec: ipfsStorage: 2Gi clusterStorage: 1Gi + clusterApi: + strategy: none + gateway: + strategy: none public: false replicas: 1 follows: [] diff --git a/examples/ipfs.yaml b/examples/ipfs.yaml index 3a5d27eb..4a12af6d 100644 --- a/examples/ipfs.yaml +++ b/examples/ipfs.yaml @@ -7,6 +7,10 @@ spec: url: apps.jephilli-4-11-04-28-0655.devcluster.openshift.com ipfsStorage: 50Gi clusterStorage: 5Gi + clusterApi: + strategy: LoadBalancer + gateway: + strategy: LoadBalancer public: true replicas: 2 follows: [] diff --git a/helm/ipfs-operator/crds/CustomResourceDefinition-ipfsclusters.cluster.ipfs.io.yaml b/helm/ipfs-operator/crds/CustomResourceDefinition-ipfsclusters.cluster.ipfs.io.yaml index 99b6adf6..d8abad74 100644 --- a/helm/ipfs-operator/crds/CustomResourceDefinition-ipfsclusters.cluster.ipfs.io.yaml +++ b/helm/ipfs-operator/crds/CustomResourceDefinition-ipfsclusters.cluster.ipfs.io.yaml @@ -33,10 +33,32 @@ spec: metadata: type: object spec: + description: IpfsClusterSpec defines the desired state of the IpfsCluster. properties: + clusterApi: + properties: + appendAnnotations: + additionalProperties: + type: string + type: object + strategy: + enum: + - ingress + - loadbalancer + - none + type: string + type: object clusterStorage: - type: string + anyOf: + - type: integer + - type: string + description: clusterStorage defines the amount of storage to be used + by IPFS Cluster. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true follows: + description: follows defines the list of other IPFS Clusters this + one should follow. items: properties: name: @@ -48,13 +70,58 @@ spec: - template type: object type: array + gateway: + properties: + appendAnnotations: + additionalProperties: + type: string + type: object + strategy: + enum: + - ingress + - loadbalancer + - none + type: string + type: object + ipfsResources: + description: ipfsResources specifies the resource requirements for + each IPFS container. If this value is omitted, then the operator + will automatically determine these settings based on the storage + sizes used. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object ipfsStorage: anyOf: - type: integer - type: string + description: ipfsStorage defines the total storage to be allocated + by this resource. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true networking: + description: networking defines network configuration settings. properties: circuitRelays: format: int32 @@ -63,12 +130,16 @@ spec: - circuitRelays type: object public: + description: public determines whether or not we should be exposing + this IPFS Cluster to the public. type: boolean replicas: + description: replicas sets the number of replicas of IPFS Cluster + nodes we should be running. format: int32 type: integer reprovider: - description: Reprovider Describes the settings that each IPFS node + description: reprovider Describes the settings that each IPFS node should use when reproviding content. properties: interval: @@ -85,6 +156,8 @@ spec: type: string type: object url: + description: url defines the URL to be using as an ingress controller. + Reprovider Describes the settings that each IPFS node type: string required: - clusterStorage