Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ephemeral option for zk storage. #284

Merged
merged 6 commits into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ generate:

# Generate manifests e.g. CRD, RBAC etc.
manifests:
rm -rf generated-check/api
controller-gen $(CRD_OPTIONS) rbac:roleName=solr-operator-role webhook paths="./..." output:rbac:artifacts:config=$(or $(TMP_CONFIG_OUTPUT_DIRECTORY),config)/rbac output:crd:artifacts:config=$(or $(TMP_CONFIG_OUTPUT_DIRECTORY),config)/crd/bases
CONFIG_DIRECTORY=$(or $(TMP_CONFIG_OUTPUT_DIRECTORY),config) HELM_DIRECTORY=$(or $(TMP_HELM_OUTPUT_DIRECTORY),helm) ./hack/config/copy_crds_roles_helm.sh
CONFIG_DIRECTORY=$(or $(TMP_CONFIG_OUTPUT_DIRECTORY),config) ./hack/config/add_crds_roles_headers.sh
Expand Down
10 changes: 8 additions & 2 deletions api/v1beta1/solrcloud_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ func (ref *ZookeeperRef) withDefaults() (changed bool) {
changed = ref.ConnectionInfo.withDefaults() || changed
}
if ref.ProvidedZookeeper != nil {
changed = ref.ProvidedZookeeper.withDefaults() || changed
changed = ref.ProvidedZookeeper.WithDefaults() || changed
}
return changed
}
Expand Down Expand Up @@ -600,9 +600,15 @@ type ZookeeperSpec struct {

// Persistence is the configuration for zookeeper persistent layer.
// PersistentVolumeClaimSpec and VolumeReclaimPolicy can be specified in here.
// At anypoint only one of Persistence or Ephemeral should be present in the manifest
// +optional
Persistence *zk.Persistence `json:"persistence,omitempty"`

// Ephemeral is the configuration which helps create ephemeral storage
// At anypoint only one of Persistence or Ephemeral should be present in the manifest
// +optional
Ephemeral *zk.Ephemeral `json:"ephemeral,omitempty"`

// Pod resources for zookeeper pod
// +optional
ZookeeperPod ZookeeperPodPolicy `json:"zookeeperPodPolicy,omitempty"`
Expand All @@ -622,7 +628,7 @@ type ZookeeperSpec struct {
ReadOnlyACL *ZookeeperACL `json:"readOnlyAcl,omitempty"`
}

func (z *ZookeeperSpec) withDefaults() (changed bool) {
func (z *ZookeeperSpec) WithDefaults() (changed bool) {
if z.Replicas == nil {
changed = true
r := DefaultZkReplicas
Expand Down
5 changes: 5 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion config/crd/bases/solr.apache.org_solrclouds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4748,6 +4748,24 @@ spec:
chroot:
description: The ChRoot to connect solr at
type: string
ephemeral:
description: Ephemeral is the configuration which helps create ephemeral storage At anypoint only one of Persistence or Ephemeral should be present in the manifest
properties:
emptydirvolumesource:
description: EmptyDirVolumeSource is optional and this will create the emptydir volume It has two parameters Medium and SizeLimit which are optional as well Medium specifies What type of storage medium should back this directory. SizeLimit specifies Total amount of local storage required for this EmptyDir volume.
properties:
medium:
description: 'What type of storage medium should back this directory. The default is "" which means to use the node''s default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
type: string
sizeLimit:
anyOf:
- type: integer
- type: string
description: 'Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir'
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
type: object
type: object
image:
description: Image of Zookeeper to run
properties:
Expand All @@ -4762,7 +4780,7 @@ spec:
type: string
type: object
persistence:
description: Persistence is the configuration for zookeeper persistent layer. PersistentVolumeClaimSpec and VolumeReclaimPolicy can be specified in here.
description: Persistence is the configuration for zookeeper persistent layer. PersistentVolumeClaimSpec and VolumeReclaimPolicy can be specified in here. At anypoint only one of Persistence or Ephemeral should be present in the manifest
properties:
reclaimPolicy:
description: VolumeReclaimPolicy is a zookeeper operator configuration. If it's set to Delete, the corresponding PVCs will be deleted by the operator when zookeeper cluster is deleted. The default value is Retain.
Expand Down
6 changes: 6 additions & 0 deletions controllers/controller_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
b64 "encoding/base64"
"fmt"
"github.com/apache/solr-operator/controllers/util"
zkv1beta1 "github.com/pravega/zookeeper-operator/pkg/apis/zookeeper/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"reflect"
"strings"
Expand Down Expand Up @@ -488,6 +489,9 @@ func cleanupTest(g *gomega.GomegaWithT, namespace string) {
// Solr Operator CRDs, modify this list whenever CRDs are added/deleted
&solr.SolrCloud{}, &solr.SolrBackup{}, &solr.SolrPrometheusExporter{},

// Dependency CRDs
&zkv1beta1.ZookeeperCluster{},

// All dependent Kubernetes types, in order of dependence (deployment then replicaSet then pod)
&corev1.ConfigMap{}, &batchv1.Job{}, &extv1.Ingress{},
&corev1.PersistentVolumeClaim{}, &corev1.PersistentVolume{},
Expand Down Expand Up @@ -677,6 +681,8 @@ var (
}
one = int64(1)
two = int64(2)
four = int32(4)
five = int32(5)
podSecurityContext = corev1.PodSecurityContext{
RunAsUser: &one,
RunAsGroup: &two,
Expand Down
86 changes: 2 additions & 84 deletions controllers/solrcloud_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ var (
cloudHsKey = types.NamespacedName{Name: "foo-clo-solrcloud-headless", Namespace: "default"}
cloudIKey = types.NamespacedName{Name: "foo-clo-solrcloud-common", Namespace: "default"}
cloudCMKey = types.NamespacedName{Name: "foo-clo-solrcloud-configmap", Namespace: "default"}
cloudZkKey = types.NamespacedName{Name: "foo-clo-solrcloud-zookeeper", Namespace: "default"}
)

func TestCloudReconcile(t *testing.T) {
Expand Down Expand Up @@ -319,90 +320,6 @@ func TestCustomKubeOptionsCloudReconcile(t *testing.T) {
testMapsEqual(t, "common service annotations", testHeadlessServiceAnnotations, headlessService.Annotations)
}

func TestCloudWithProvidedZookeeperReconcile(t *testing.T) {
UseZkCRD(true)
g := gomega.NewGomegaWithT(t)
instance := &solr.SolrCloud{
ObjectMeta: metav1.ObjectMeta{Name: expectedCloudRequest.Name, Namespace: expectedCloudRequest.Namespace},
Spec: solr.SolrCloudSpec{
ZookeeperRef: &solr.ZookeeperRef{
ProvidedZookeeper: &solr.ZookeeperSpec{
ChRoot: "a-ch/root",
},
},
UpdateStrategy: solr.SolrUpdateStrategy{
Method: solr.ManualUpdate,
},
},
}

// Setup the Manager and Controller. Wrap the Controller Reconcile function so it writes each request to a
// channel when it is finished.
mgr, err := manager.New(testCfg, manager.Options{})
g.Expect(err).NotTo(gomega.HaveOccurred())
testClient = mgr.GetClient()

// Blocked until https://github.com/pravega/zookeeper-operator/pull/99 is merged
//g.Expect(zookeepercluster.AddZookeeperReconciler(mgr)).NotTo(gomega.HaveOccurred())

solrCloudReconciler := &SolrCloudReconciler{
Client: testClient,
Log: ctrl.Log.WithName("controllers").WithName("SolrCloud"),
}
newRec, requests := SetupTestReconcile(solrCloudReconciler)
g.Expect(solrCloudReconciler.SetupWithManagerAndReconciler(mgr, newRec)).NotTo(gomega.HaveOccurred())

stopMgr, mgrStopped := StartTestManager(mgr, g)

defer func() {
close(stopMgr)
mgrStopped.Wait()
}()

cleanupTest(g, instance.Namespace)

// Create the SolrCloud object and expect the Reconcile and StatefulSet to be created
err = testClient.Create(context.TODO(), instance)
g.Expect(err).NotTo(gomega.HaveOccurred())
defer testClient.Delete(context.TODO(), instance)
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedCloudRequest)))
// Add an additional check for reconcile, so that the zkCluster will have been created
// Otherwise the reconciler will have 'blockReconciliationOfStatefulSet' set to true, and the stateful set will not be created
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedCloudRequest)))
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedCloudRequest)))

g.Eventually(func() error { return testClient.Get(context.TODO(), expectedCloudRequest.NamespacedName, instance) }, timeout).Should(gomega.Succeed())

// Check that the ZkConnectionInformation is correct
expectedZkConnStr := "foo-clo-solrcloud-zookeeper-0.foo-clo-solrcloud-zookeeper-headless.default.svc.cluster.local:2181,foo-clo-solrcloud-zookeeper-1.foo-clo-solrcloud-zookeeper-headless.default.svc.cluster.local:2181,foo-clo-solrcloud-zookeeper-2.foo-clo-solrcloud-zookeeper-headless.default.svc.cluster.local:2181"
assert.Equal(t, expectedZkConnStr, instance.Status.ZookeeperConnectionInfo.InternalConnectionString, "Wrong zkConnectionString in status")
assert.Equal(t, "/a-ch/root", instance.Status.ZookeeperConnectionInfo.ChRoot, "Wrong zk chRoot in status")
assert.Nil(t, instance.Status.ZookeeperConnectionInfo.ExternalConnectionString, "Since a provided zk is used, the externalConnectionString in the status should be Nil")

// Check that the statefulSet has been created, using the given chRoot
statefulSet := expectStatefulSet(t, g, requests, expectedCloudRequest, cloudSsKey)

assert.Equal(t, 1, len(statefulSet.Spec.Template.Spec.Containers), "Solr StatefulSet requires a container.")
expectedZKHost := expectedZkConnStr + "/a-ch/root"
expectedEnvVars := map[string]string{
"ZK_HOST": expectedZKHost,
"SOLR_HOST": "$(POD_HOSTNAME)." + cloudHsKey.Name + "." + cloudHsKey.Namespace,
"ZK_SERVER": expectedZkConnStr,
"ZK_CHROOT": "/a-ch/root",
"SOLR_PORT": "8983",
"GC_TUNE": "",
}
expectedStatefulSetAnnotations := map[string]string{util.SolrZKConnectionStringAnnotation: expectedZKHost}
testPodEnvVariables(t, expectedEnvVars, statefulSet.Spec.Template.Spec.Containers[0].Env)
testMapsEqual(t, "statefulSet annotations", expectedStatefulSetAnnotations, statefulSet.Annotations)
assert.EqualValues(t, []string{"sh", "-c", "solr zk ls ${ZK_CHROOT} -z ${ZK_SERVER} || solr zk mkroot ${ZK_CHROOT} -z ${ZK_SERVER}"}, statefulSet.Spec.Template.Spec.Containers[0].Lifecycle.PostStart.Exec.Command, "Incorrect post-start command")
assert.Empty(t, statefulSet.Spec.Template.Spec.ServiceAccountName, "No custom serviceAccountName specified, so the field should be empty.")

// Check the update strategy
assert.EqualValues(t, appsv1.OnDeleteStatefulSetStrategyType, statefulSet.Spec.UpdateStrategy.Type, "Incorrect statefulset update strategy")
assert.EqualValues(t, appsv1.ParallelPodManagement, statefulSet.Spec.PodManagementPolicy, "Incorrect statefulset pod management policy")
}

func TestCloudWithExternalZookeeperChroot(t *testing.T) {
UseZkCRD(true)
g := gomega.NewGomegaWithT(t)
Expand Down Expand Up @@ -476,6 +393,7 @@ func TestCloudWithExternalZookeeperChroot(t *testing.T) {
testPodEnvVariables(t, expectedEnvVars, statefulSet.Spec.Template.Spec.Containers[0].Env)
testMapsEqual(t, "statefulSet annotations", expectedStatefulSetAnnotations, statefulSet.Annotations)
assert.EqualValues(t, []string{"sh", "-c", "solr zk ls ${ZK_CHROOT} -z ${ZK_SERVER} || solr zk mkroot ${ZK_CHROOT} -z ${ZK_SERVER}"}, statefulSet.Spec.Template.Spec.Containers[0].Lifecycle.PostStart.Exec.Command, "Incorrect post-start command")
assert.Empty(t, statefulSet.Spec.Template.Spec.ServiceAccountName, "No custom serviceAccountName specified, so the field should be empty.")
}

func TestDefaults(t *testing.T) {
Expand Down
Loading