diff --git a/api/v1/seaweed_types.go b/api/v1/seaweed_types.go index 3a6fa36..0ac78d2 100644 --- a/api/v1/seaweed_types.go +++ b/api/v1/seaweed_types.go @@ -118,6 +118,15 @@ type MasterSpec struct { // +kubebuilder:validation:Minimum=1 Replicas int32 `json:"replicas"` Service *ServiceSpec `json:"service,omitempty"` + + // Config in raw toml string + Config *string `json:"config,omitempty"` + + VolumePreallocate *bool `json:"volumePreallocate,omitempty"` + VolumeSizeLimitMB *int32 `json:"volumeSizeLimitMB,omitempty"` + GarbageThreshold *float64 `json:"garbageThreshold,omitempty"` + PulseSeconds *int32 `json:"pulseSeconds,omitempty"` + DefaultReplication *string `json:"defaultReplication,omitempty"` } // VolumeSpec is the spec for volume servers @@ -129,6 +138,8 @@ type VolumeSpec struct { // +kubebuilder:validation:Minimum=1 Replicas int32 `json:"replicas"` Service *ServiceSpec `json:"service,omitempty"` + // Config in raw toml string + Config *string `json:"config,omitempty"` } // FilerSpec is the spec for filers @@ -140,6 +151,8 @@ type FilerSpec struct { // +kubebuilder:validation:Minimum=1 Replicas int32 `json:"replicas"` Service *ServiceSpec `json:"service,omitempty"` + // Config in raw toml string + Config *string `json:"config,omitempty"` } // ComponentSpec is the base spec of each component, the fields should always accessed by the BasicSpec() method to respect the cluster-level properties diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 692e03a..52721d3 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -132,6 +132,11 @@ func (in *FilerSpec) DeepCopyInto(out *FilerSpec) { *out = new(ServiceSpec) (*in).DeepCopyInto(*out) } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FilerSpec. @@ -154,6 +159,36 @@ func (in *MasterSpec) DeepCopyInto(out *MasterSpec) { *out = new(ServiceSpec) (*in).DeepCopyInto(*out) } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = new(string) + **out = **in + } + if in.VolumePreallocate != nil { + in, out := &in.VolumePreallocate, &out.VolumePreallocate + *out = new(bool) + **out = **in + } + if in.VolumeSizeLimitMB != nil { + in, out := &in.VolumeSizeLimitMB, &out.VolumeSizeLimitMB + *out = new(int32) + **out = **in + } + if in.GarbageThreshold != nil { + in, out := &in.GarbageThreshold, &out.GarbageThreshold + *out = new(float64) + **out = **in + } + if in.PulseSeconds != nil { + in, out := &in.PulseSeconds, &out.PulseSeconds + *out = new(int32) + **out = **in + } + if in.DefaultReplication != nil { + in, out := &in.DefaultReplication, &out.DefaultReplication + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MasterSpec. @@ -363,6 +398,11 @@ func (in *VolumeSpec) DeepCopyInto(out *VolumeSpec) { *out = new(ServiceSpec) (*in).DeepCopyInto(*out) } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSpec. diff --git a/controllers/controller_filer_statefulset.go b/controllers/controller_filer_statefulset.go index e0cc7c7..5aaee49 100644 --- a/controllers/controller_filer_statefulset.go +++ b/controllers/controller_filer_statefulset.go @@ -18,11 +18,30 @@ func (r *SeaweedReconciler) createFilerStatefulSet(m *seaweedv1.Seaweed) *appsv1 enableServiceLinks := false filerPodSpec := m.BaseFilerSpec().BuildPodSpec() + filerPodSpec.Volumes = []corev1.Volume{ + { + Name: "filer-config", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: m.Name + "-filer", + }, + }, + }, + }, + } filerPodSpec.EnableServiceLinks = &enableServiceLinks filerPodSpec.Containers = []corev1.Container{{ Name: "seaweedfs", Image: m.Spec.Image, ImagePullPolicy: corev1.PullIfNotPresent, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "filer-config", + ReadOnly: true, + MountPath: "/etc/seaweedfs/filer.toml", + }, + }, Env: []corev1.EnvVar{ { Name: "POD_IP", diff --git a/controllers/controller_master_configmap.go b/controllers/controller_master_configmap.go new file mode 100644 index 0000000..f6f481d --- /dev/null +++ b/controllers/controller_master_configmap.go @@ -0,0 +1,31 @@ +package controllers + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + seaweedv1 "github.com/seaweedfs/seaweedfs-operator/api/v1" +) + +func (r *SeaweedReconciler) createMasterConfigMap(m *seaweedv1.Seaweed) *corev1.ConfigMap { + labels := labelsForMaster(m.Name) + + toml := "" + if m.Spec.Master.Config != nil { + toml = *m.Spec.Master.Config + } + + dep := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: m.Name + "-master", + Namespace: m.Namespace, + Labels: labels, + }, + Data: map[string]string{ + "master.toml": toml, + }, + } + // Set master instance as the owner and controller + // ctrl.SetControllerReference(m, dep, r.Scheme) + return dep +} diff --git a/controllers/controller_master_statefulset.go b/controllers/controller_master_statefulset.go index 80ff4cb..a43ca8b 100644 --- a/controllers/controller_master_statefulset.go +++ b/controllers/controller_master_statefulset.go @@ -2,6 +2,7 @@ package controllers import ( "fmt" + "strings" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -11,6 +12,30 @@ import ( seaweedv1 "github.com/seaweedfs/seaweedfs-operator/api/v1" ) +func buildMasterStartupScript(m *seaweedv1.Seaweed) string { + command := []string{"weed", "master"} + spec := m.Spec.Master + if spec.VolumePreallocate != nil && *spec.VolumePreallocate { + command = append(command, "-volumePreallocate") + } + + if spec.VolumeSizeLimitMB != nil { + command = append(command, fmt.Sprintf("-volumeSizeLimitMB=%d", *spec.VolumeSizeLimitMB)) + } + + if spec.GarbageThreshold != nil { + command = append(command, fmt.Sprintf("-garbageThreshold=%f", *spec.GarbageThreshold)) + } + + if spec.PulseSeconds != nil { + command = append(command, fmt.Sprintf("-pulseSeconds=%d", *spec.PulseSeconds)) + } + + command = append(command, fmt.Sprintf("-ip=$(POD_NAME).%s-master", m.Name)) + command = append(command, fmt.Sprintf("-peers=%s", getMasterPeersString(m.Name, spec.Replicas))) + return strings.Join(command, " ") +} + func (r *SeaweedReconciler) createMasterStatefulSet(m *seaweedv1.Seaweed) *appsv1.StatefulSet { labels := labelsForMaster(m.Name) replicas := m.Spec.Master.Replicas @@ -18,19 +43,35 @@ func (r *SeaweedReconciler) createMasterStatefulSet(m *seaweedv1.Seaweed) *appsv enableServiceLinks := false masterPodSpec := m.BaseMasterSpec().BuildPodSpec() + masterPodSpec.Volumes = []corev1.Volume{ + { + Name: "master-config", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: m.Name + "-master", + }, + }, + }, + }, + } masterPodSpec.EnableServiceLinks = &enableServiceLinks masterPodSpec.Containers = []corev1.Container{{ Name: "seaweedfs", Image: m.Spec.Image, ImagePullPolicy: corev1.PullIfNotPresent, Env: append(m.BaseMasterSpec().Env(), kubernetesEnvVars...), + VolumeMounts: []corev1.VolumeMount{ + { + Name: "master-config", + ReadOnly: true, + MountPath: "/etc/seaweedfs/master.toml", + }, + }, Command: []string{ "/bin/sh", "-ec", - fmt.Sprintf("sleep 60; weed master -volumePreallocate -volumeSizeLimitMB=1000 %s %s", - fmt.Sprintf("-ip=$(POD_NAME).%s-master", m.Name), - fmt.Sprintf("-peers=%s", getMasterPeersString(m.Name, m.Spec.Master.Replicas)), - ), + buildMasterStartupScript(m), }, Ports: []corev1.ContainerPort{ { diff --git a/go.sum b/go.sum index 89e74ab..e4a458d 100644 --- a/go.sum +++ b/go.sum @@ -411,6 +411,7 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/api v0.19.3 h1:GN6ntFnv44Vptj/b+OnMW7FmzkpDoIDLZRvKX3XH9aU= k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8= k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA=