Skip to content

Commit e2f3b5b

Browse files
committed
add tests + ipfsResources + use resource.Quantity for storage values
Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com>
1 parent b88914b commit e2f3b5b

File tree

7 files changed

+193
-17
lines changed

7 files changed

+193
-17
lines changed

api/v1alpha1/ipfscluster_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ type IpfsClusterSpec struct {
7575
// ipfsStorage defines the total storage to be allocated by this resource.
7676
IpfsStorage resource.Quantity `json:"ipfsStorage"`
7777
// clusterStorage defines the amount of storage to be used by IPFS Cluster.
78-
ClusterStorage string `json:"clusterStorage"`
78+
ClusterStorage resource.Quantity `json:"clusterStorage"`
7979
// replicas sets the number of replicas of IPFS Cluster nodes we should be running.
8080
Replicas int32 `json:"replicas"`
8181
// networking defines network configuration settings.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 9 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/cluster.ipfs.io_ipfsclusters.yaml

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,19 @@ spec:
3333
metadata:
3434
type: object
3535
spec:
36+
description: IpfsClusterSpec defines the desired state of the IpfsCluster.
3637
properties:
3738
clusterStorage:
38-
type: string
39+
anyOf:
40+
- type: integer
41+
- type: string
42+
description: clusterStorage defines the amount of storage to be used
43+
by IPFS Cluster.
44+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
45+
x-kubernetes-int-or-string: true
3946
follows:
47+
description: follows defines the list of other IPFS Clusters this
48+
one should follow.
4049
items:
4150
properties:
4251
name:
@@ -48,13 +57,45 @@ spec:
4857
- template
4958
type: object
5059
type: array
60+
ipfsResources:
61+
description: ipfsResources specifies the resource requirements for
62+
each IPFS container. If this value is omitted, then the operator
63+
will automatically determine these settings based on the storage
64+
sizes used.
65+
properties:
66+
limits:
67+
additionalProperties:
68+
anyOf:
69+
- type: integer
70+
- type: string
71+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
72+
x-kubernetes-int-or-string: true
73+
description: 'Limits describes the maximum amount of compute resources
74+
allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
75+
type: object
76+
requests:
77+
additionalProperties:
78+
anyOf:
79+
- type: integer
80+
- type: string
81+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
82+
x-kubernetes-int-or-string: true
83+
description: 'Requests describes the minimum amount of compute
84+
resources required. If Requests is omitted for a container,
85+
it defaults to Limits if that is explicitly specified, otherwise
86+
to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
87+
type: object
88+
type: object
5189
ipfsStorage:
5290
anyOf:
5391
- type: integer
5492
- type: string
93+
description: ipfsStorage defines the total storage to be allocated
94+
by this resource.
5595
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
5696
x-kubernetes-int-or-string: true
5797
networking:
98+
description: networking defines network configuration settings.
5899
properties:
59100
circuitRelays:
60101
format: int32
@@ -63,12 +104,16 @@ spec:
63104
- circuitRelays
64105
type: object
65106
public:
107+
description: public determines whether or not we should be exposing
108+
this IPFS Cluster to the public.
66109
type: boolean
67110
replicas:
111+
description: replicas sets the number of replicas of IPFS Cluster
112+
nodes we should be running.
68113
format: int32
69114
type: integer
70115
reprovider:
71-
description: Reprovider Describes the settings that each IPFS node
116+
description: reprovider Describes the settings that each IPFS node
72117
should use when reproviding content.
73118
properties:
74119
interval:
@@ -85,6 +130,7 @@ spec:
85130
type: string
86131
type: object
87132
url:
133+
description: url defines the URL to be using as an ingress controller.
88134
type: string
89135
required:
90136
- clusterStorage

controllers/ipfscluster_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ func (r *IpfsClusterReconciler) createTrackedObjects(
152152
[]byte(privateString),
153153
peerID.String(),
154154
)
155-
mutSts := r.statefulSet(instance, &sts, svcName, secConfigName, cmScriptName)
155+
mutSts := r.StatefulSet(instance, &sts, svcName, secConfigName, cmScriptName)
156156

157157
trackedObjects := map[client.Object]controllerutil.MutateFn{
158158
&sa: mutsa,

controllers/ipfscluster_controller_test.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import (
77

88
. "github.com/onsi/ginkgo/v2"
99
. "github.com/onsi/gomega"
10+
appsv1 "k8s.io/api/apps/v1"
1011
v1 "k8s.io/api/core/v1"
12+
"k8s.io/apimachinery/pkg/api/resource"
1113
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1214

1315
"github.com/redhat-et/ipfs-operator/api/v1alpha1"
@@ -100,6 +102,121 @@ var _ = Describe("IPFS Reconciler", func() {
100102
})
101103
})
102104

105+
var _ = Describe("StatefulSet creation", func() {
106+
var ipfsReconciler *controllers.IpfsClusterReconciler
107+
var ipfs *v1alpha1.IpfsCluster
108+
var sts *appsv1.StatefulSet
109+
// var configMapScripts *v1.ConfigMap
110+
// var secret *v1.Secret
111+
// var svc *v1.Service
112+
// var ctx context.Context
113+
114+
const (
115+
myName = "my-fav-ipfs-node"
116+
scriptsName = "my-scripts"
117+
secretName = "my-secret"
118+
svcName = "my-svc"
119+
namespace = "test"
120+
)
121+
BeforeEach(func() {
122+
// ctx = context.TODO()
123+
ipfsReconciler = &controllers.IpfsClusterReconciler{
124+
Scheme: k8sClient.Scheme(),
125+
Client: k8sClient,
126+
}
127+
// configMapScripts = &v1.ConfigMap{ObjectMeta: metav1.ObjectMeta{
128+
// Name: scriptsName,
129+
// }}
130+
ipfs = &v1alpha1.IpfsCluster{
131+
ObjectMeta: metav1.ObjectMeta{
132+
Name: myName,
133+
Namespace: namespace,
134+
},
135+
}
136+
sts = &appsv1.StatefulSet{}
137+
// secret = &v1.Secret{ObjectMeta: metav1.ObjectMeta{Name: secretName}}
138+
// svc = &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: svcName}}
139+
})
140+
141+
When("ipfsResources is specified", func() {
142+
var cpuLimit = resource.NewQuantity(58, "m")
143+
var memoryLimit = resource.NewQuantity(10000, "m")
144+
var cpu = resource.NewQuantity(20, "m")
145+
var memory = resource.NewQuantity(40, "M")
146+
BeforeEach(func() {
147+
ipfs.Spec.IPFSResources = &v1.ResourceRequirements{
148+
Limits: v1.ResourceList{
149+
v1.ResourceLimitsCPU: *cpuLimit,
150+
v1.ResourceLimitsMemory: *memoryLimit,
151+
},
152+
Requests: v1.ResourceList{
153+
v1.ResourceCPU: *cpu,
154+
v1.ResourceMemory: *memory,
155+
},
156+
}
157+
ipfs.Spec.ClusterStorage = *resource.NewQuantity(1, "Gi")
158+
ipfs.Spec.IpfsStorage = *resource.NewQuantity(1, "Gi")
159+
})
160+
It("uses the IPFSCluster's IPFSResources setting", func() {
161+
mutFn := ipfsReconciler.StatefulSet(ipfs, sts, svcName, secretName, scriptsName)
162+
err := mutFn()
163+
Expect(err).NotTo(HaveOccurred())
164+
165+
// find IPFS Container
166+
var ipfsContainer v1.Container
167+
for _, container := range sts.Spec.Template.Spec.Containers {
168+
if container.Name == controllers.ContainerIPFS {
169+
ipfsContainer = container
170+
break
171+
}
172+
}
173+
Expect(ipfsContainer).NotTo(BeNil())
174+
ipfsResources := ipfsContainer.Resources
175+
Expect(ipfsResources.Limits).NotTo(BeEmpty())
176+
Expect(ipfsResources.Limits[v1.ResourceLimitsCPU]).To(Equal(*cpuLimit))
177+
Expect(ipfsResources.Limits[v1.ResourceLimitsMemory]).To(Equal(*memoryLimit))
178+
Expect(ipfsResources.Requests).NotTo(BeEmpty())
179+
Expect(ipfsResources.Requests[v1.ResourceCPU]).To(Equal(*cpu))
180+
Expect(ipfsResources.Requests[v1.ResourceMemory]).To(Equal(*memory))
181+
})
182+
})
183+
184+
When("ipfsResources is omitted", func() {
185+
BeforeEach(func() {
186+
ipfs.Spec.ClusterStorage = *resource.NewQuantity(5, "T")
187+
ipfs.Spec.IpfsStorage = *resource.NewQuantity(16, "T")
188+
})
189+
It("automatically computes resources requirements", func() {
190+
mutFn := ipfsReconciler.StatefulSet(ipfs, sts, svcName, secretName, scriptsName)
191+
err := mutFn()
192+
Expect(err).NotTo(HaveOccurred())
193+
194+
// find IPFS Container
195+
var ipfsContainer v1.Container
196+
for _, container := range sts.Spec.Template.Spec.Containers {
197+
if container.Name == controllers.ContainerIPFS {
198+
ipfsContainer = container
199+
break
200+
}
201+
}
202+
Expect(ipfsContainer).NotTo(BeNil())
203+
ipfsResources := ipfsContainer.Resources
204+
Expect(ipfsResources.Limits).NotTo(BeEmpty())
205+
Expect(ipfsResources.Requests).NotTo(BeEmpty())
206+
207+
cpuLimit := ipfsResources.Limits[v1.ResourceLimitsCPU]
208+
memoryLimit := ipfsResources.Limits[v1.ResourceLimitsMemory]
209+
Expect(cpuLimit.Value()).NotTo(Equal(0))
210+
Expect(memoryLimit.Value()).NotTo(Equal(0))
211+
212+
cpu := ipfsResources.Requests[v1.ResourceCPU]
213+
memory := ipfsResources.Requests[v1.ResourceMemory]
214+
Expect(cpu.Value()).NotTo(Equal(0))
215+
Expect(memory.Value()).NotTo(Equal(0))
216+
})
217+
})
218+
})
219+
103220
// The k8s client will encode and copy data from the StringData to Data
104221
// This function mimics the behavior for tests.
105222
func secretStringToData(secret *v1.Secret) {

controllers/statefulset.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
appsv1 "k8s.io/api/apps/v1"
88
corev1 "k8s.io/api/core/v1"
9-
"k8s.io/apimachinery/pkg/api/resource"
109
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1110
"k8s.io/apimachinery/pkg/util/intstr"
1211
ctrl "sigs.k8s.io/controller-runtime"
@@ -36,6 +35,13 @@ const (
3635
portHTTP = 8080
3736
)
3837

38+
// Defines common names
39+
const (
40+
ContainerIPFS = "ipfs"
41+
ContainerIPFSCluster = "ipfs-cluster"
42+
ContainerInitIPFS = "configure-ipfs"
43+
)
44+
3945
// Misclaneous constants.
4046
const (
4147
// notDNSPattern Defines a ReGeX pattern to match non-DNS names.
@@ -51,14 +57,12 @@ const (
5157
ipfsImage = "docker.io/ipfs/kubo:v0.14.0"
5258
)
5359

54-
// statefulSet Returns a mutate function that creates a statefulSet for the
60+
// StatefulSet Returns a mutate function that creates a StatefulSet for the
5561
// given IPFS cluster.
5662
// FIXME: break this function up to use createOrUpdate and set values in the struct line-by-line
5763
//
58-
// instead of setting the entire thing all at once.
59-
//
6064
// nolint:funlen // Function is long due to Kube resource definitions
61-
func (r *IpfsClusterReconciler) statefulSet(m *clusterv1alpha1.IpfsCluster,
65+
func (r *IpfsClusterReconciler) StatefulSet(m *clusterv1alpha1.IpfsCluster,
6266
sts *appsv1.StatefulSet,
6367
serviceName string,
6468
secretName string,
@@ -68,7 +72,7 @@ func (r *IpfsClusterReconciler) statefulSet(m *clusterv1alpha1.IpfsCluster,
6872

6973
//
7074
var ipfsResources corev1.ResourceRequirements
71-
if m.Spec.IPFSResources == nil {
75+
if m.Spec.IPFSResources != nil {
7276
ipfsResources = *m.Spec.IPFSResources
7377
} else {
7478
ipfsResources = utils.IPFSContainerResources(m.Spec.IpfsStorage.Value())
@@ -96,7 +100,7 @@ func (r *IpfsClusterReconciler) statefulSet(m *clusterv1alpha1.IpfsCluster,
96100
ServiceAccountName: ssName,
97101
InitContainers: []corev1.Container{
98102
{
99-
Name: "configure-ipfs",
103+
Name: ContainerInitIPFS,
100104
Image: ipfsImage,
101105
Command: []string{
102106
"sh",
@@ -120,7 +124,7 @@ func (r *IpfsClusterReconciler) statefulSet(m *clusterv1alpha1.IpfsCluster,
120124
},
121125
Containers: []corev1.Container{
122126
{
123-
Name: "ipfs",
127+
Name: ContainerIPFS,
124128
Image: ipfsImage,
125129
ImagePullPolicy: corev1.PullIfNotPresent,
126130
Env: []corev1.EnvVar{
@@ -175,7 +179,7 @@ func (r *IpfsClusterReconciler) statefulSet(m *clusterv1alpha1.IpfsCluster,
175179
Resources: ipfsResources,
176180
},
177181
{
178-
Name: "ipfs-cluster",
182+
Name: ContainerIPFSCluster,
179183
Image: ipfsClusterImage,
180184
ImagePullPolicy: corev1.PullIfNotPresent,
181185
Command: []string{
@@ -301,7 +305,7 @@ func (r *IpfsClusterReconciler) statefulSet(m *clusterv1alpha1.IpfsCluster,
301305
},
302306
Resources: corev1.ResourceRequirements{
303307
Requests: corev1.ResourceList{
304-
corev1.ResourceStorage: resource.MustParse(m.Spec.ClusterStorage),
308+
corev1.ResourceStorage: m.Spec.ClusterStorage,
305309
},
306310
},
307311
},

test-kuttl/e2e/ipfs/15-test-data-creation.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ main() {
2121
log "reading a file from ${ipfsClusterPodname2}"
2222
ipfsCommand="ipfs get --output /tmp/myfile.txt -- ${myCID}"
2323
kubectl exec -n "${NAMESPACE}" "${ipfsClusterPodname2}" -c ipfs -- sh -c "${ipfsCommand}"
24-
}
24+
}
25+
26+
main

0 commit comments

Comments
 (0)