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

added Cluster Settings v2 Update for Cassandra #525

Merged
merged 1 commit into from
Aug 16, 2023
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
19 changes: 19 additions & 0 deletions apis/clusters/v1beta1/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ type Cluster struct {
SLATier string `json:"slaTier,omitempty"`

TwoFactorDelete []*TwoFactorDelete `json:"twoFactorDelete,omitempty"`

Description string `json:"description,omitempty"`
}

type ClusterStatus struct {
Expand Down Expand Up @@ -150,6 +152,7 @@ func (c *Cluster) IsEqual(cluster Cluster) bool {
c.PCICompliance == cluster.PCICompliance &&
c.PrivateNetworkCluster == cluster.PrivateNetworkCluster &&
c.SLATier == cluster.SLATier &&
c.Description == cluster.Description &&
c.IsTwoFactorDeleteEqual(cluster.TwoFactorDelete)
}

Expand Down Expand Up @@ -181,6 +184,22 @@ func (c *Cluster) TwoFactorDeletesToInstAPI() (TFDs []*models.TwoFactorDelete) {
return
}

func (c *Cluster) ClusterSettingsUpdateToInstAPI() (*models.ClusterSettings, error) {
if len(c.TwoFactorDelete) > 1 {
return nil, models.ErrOnlyOneEntityTwoFactorDelete
}

iTFD := &models.TwoFactorDelete{}
for _, tfd := range c.TwoFactorDelete {
iTFD = tfd.ToInstAPI()
}

return &models.ClusterSettings{
Description: c.Description,
TwoFactorDelete: iTFD,
}, nil
}

func (c *Cluster) TwoFactorDeleteToInstAPIv1() *models.TwoFactorDeleteV1 {
if len(c.TwoFactorDelete) == 0 {
return nil
Expand Down
12 changes: 9 additions & 3 deletions apis/clusters/v1beta1/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,20 @@ func validateAppVersion(
}

func validateTwoFactorDelete(new, old []*TwoFactorDelete) error {
if len(new) != 0 && len(old) == 0 {
if len(old) == 0 && len(new) == 0 ||
len(old) == 0 && len(new) == 1 {
Comment on lines +145 to +146
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of double check, you can do:

if len(old) == 0 && len(new) < 2 {
    return nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just want to explicitly show that the new object can be validated successfully as if it were empty or with only one object populated. len(new) < 2 is cleaner but less readable imho.

return nil
}

if len(new) > 1 {
return models.ErrOnlyOneEntityTwoFactorDelete
}

if len(old) != len(new) {
return models.ErrImmutableTwoFactorDelete
}
if len(old) != 0 &&
*old[0] != *new[0] {

if *old[0] != *new[0] {
return models.ErrImmutableTwoFactorDelete
}

Expand Down
4 changes: 3 additions & 1 deletion config/crd/bases/clusters.instaclustr.com_cassandras.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ spec:
- replicationFactor
type: object
type: array
description:
type: string
luceneEnabled:
type: boolean
name:
Expand Down Expand Up @@ -172,7 +174,7 @@ spec:
- email
type: object
type: array
userRef:
userRefs:
items:
properties:
name:
Expand Down
2 changes: 2 additions & 0 deletions config/crd/bases/clusters.instaclustr.com_kafkaconnects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ spec:
- replicationFactor
type: object
type: array
description:
type: string
name:
description: Name [ 3 .. 32 ] characters.
type: string
Expand Down
2 changes: 2 additions & 0 deletions config/crd/bases/clusters.instaclustr.com_kafkas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ spec:
- nodesNumber
type: object
type: array
description:
type: string
karapaceRestProxy:
items:
properties:
Expand Down
2 changes: 2 additions & 0 deletions config/crd/bases/clusters.instaclustr.com_opensearches.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ spec:
- nodesNumber
type: object
type: array
description:
type: string
icuPlugin:
type: boolean
indexManagementPlugin:
Expand Down
2 changes: 2 additions & 0 deletions config/crd/bases/clusters.instaclustr.com_zookeepers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ spec:
- region
type: object
type: array
description:
type: string
name:
description: Name [ 3 .. 32 ] characters.
type: string
Expand Down
5 changes: 3 additions & 2 deletions config/samples/clusters_v1beta1_cassandra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ spec:
# - namespace: default
# name: cassandrauser-sample2
slaTier: "NON_PRODUCTION"
# twoFactorDelete:
# - email: "[email protected]"
# description: "this is a sample of description"
# twoFactorDelete:
# - email: "[email protected]"
#spark:
# - version: "2.3.2" # 3.0.1 for 4.0.4 version of Cassandra | 2.3.2 for 3.11.13 version of Cassandra

33 changes: 30 additions & 3 deletions controllers/clusters/cassandra_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,35 @@ func (r *CassandraReconciler) handleUpdateCluster(
return r.handleExternalChanges(cassandra, iCassandra, l)
}

if !cassandra.Spec.IsEqual(iCassandra.Spec) {
patch := cassandra.NewPatch()

if len(cassandra.Spec.TwoFactorDelete) != 0 && len(iCassandra.Spec.TwoFactorDelete) == 0 ||
cassandra.Spec.Description != iCassandra.Spec.Description {
settingsToInstAPI, err := cassandra.Spec.ClusterSettingsUpdateToInstAPI()
if err != nil {
l.Error(err, "Cannot convert cluster settings to Instaclustr API",
"cluster ID", cassandra.Status.ID,
"cluster spec", cassandra.Spec)

r.EventRecorder.Eventf(cassandra, models.Warning, models.UpdateFailed,
"Cannot update cluster settings. Reason: %v", err)

return models.ReconcileRequeue
}

err = r.API.UpdateClusterSettings(cassandra.Status.ID, settingsToInstAPI)
if err != nil {
l.Error(err, "Cannot update cluster settings",
"cluster ID", cassandra.Status.ID, "cluster spec", cassandra.Spec)

r.EventRecorder.Eventf(cassandra, models.Warning, models.UpdateFailed,
"Cannot update cluster settings. Reason: %v", err)

return models.ReconcileRequeue
}
}

if !cassandra.Spec.AreDCsEqual(iCassandra.Spec.DataCentres) {
err = r.API.UpdateCassandra(cassandra.Status.ID, cassandra.Spec.NewDCsUpdate())
if err != nil {
l.Error(err, "Cannot update cluster",
Expand Down Expand Up @@ -357,7 +385,6 @@ func (r *CassandraReconciler) handleUpdateCluster(
}
}

patch := cassandra.NewPatch()
cassandra.Annotations[models.ResourceStateAnnotation] = models.UpdatedEvent
cassandra.Annotations[models.UpdateQueuedAnnotation] = ""
err = r.Patch(ctx, cassandra, patch)
Expand Down Expand Up @@ -938,7 +965,7 @@ func (r *CassandraReconciler) newWatchStatusJob(cassandra *v1beta1.Cassandra) sc
}

if iCassandra.Status.CurrentClusterOperationStatus == models.NoOperation &&
cassandra.Annotations[models.ExternalChangesAnnotation] != models.True &&
cassandra.Annotations[models.ResourceStateAnnotation] != models.UpdatingEvent &&
cassandra.Annotations[models.UpdateQueuedAnnotation] != models.True &&
!cassandra.Spec.IsEqual(iCassandra.Spec) {
l.Info(msgExternalChanges, "instaclustr data", iCassandra.Spec, "k8s resource spec", cassandra.Spec)
Expand Down
26 changes: 26 additions & 0 deletions pkg/instaclustr/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2215,3 +2215,29 @@ func (c *Client) GetDefaultCredentialsV1(clusterID string) (string, string, erro

return creds.Username, creds.InstaclustrUserPassword, nil
}

func (c *Client) UpdateClusterSettings(clusterID string, settings *models.ClusterSettings) error {
url := fmt.Sprintf(ClusterSettingsEndpoint, c.serverHostname, clusterID)

data, err := json.Marshal(settings)
if err != nil {
return err
}

resp, err := c.DoRequest(url, http.MethodPut, data)
if err != nil {
return err
}

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}

if resp.StatusCode != http.StatusAccepted {
return fmt.Errorf("status code: %d, message: %s", resp.StatusCode, body)
}

return nil
}
3 changes: 2 additions & 1 deletion pkg/instaclustr/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import "time"

const (
DefaultTimeout = time.Second * 60
OperatorVersion = "k8s v0.0.5"
OperatorVersion = "k8s v0.1.1"
)

// constants for API v2
Expand Down Expand Up @@ -51,6 +51,7 @@ const (
NodeReloadEndpoint = "%s/cluster-management/v2/operations/applications/postgresql/nodes/v2/%s/reload"
AWSEncryptionKeyEndpoint = "/cluster-management/v2/resources/providers/aws/encryption-keys/v2/"
ListAppsVersionsEndpoint = "%s/cluster-management/v2/data-sources/applications/%s/versions/v2/"
ClusterSettingsEndpoint = "%s/cluster-management/v2/operations/clusters/v2/%s/change-settings/v2"
)

// constants for API v1
Expand Down
1 change: 1 addition & 0 deletions pkg/instaclustr/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,5 @@ type API interface {
DeleteUser(username, clusterID, app string) error
ListAppVersions(app string) ([]*models.AppVersions, error)
GetDefaultCredentialsV1(clusterID string) (string, string, error)
UpdateClusterSettings(clusterID string, settings *models.ClusterSettings) error
}
4 changes: 4 additions & 0 deletions pkg/instaclustr/mock/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,3 +347,7 @@ func (c *mockClient) DeleteUser(username, clusterID, app string) error {
func (c *mockClient) GetDefaultCredentialsV1(clusterID string) (string, string, error) {
panic("GetDefaultCredentialsV1: is not implemented")
}

func (c *mockClient) UpdateClusterSettings(clusterID string, settings *models.ClusterSettings) error {
panic("UpdateClusterSettings: is not implemented")
}
5 changes: 5 additions & 0 deletions pkg/models/apiv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,8 @@ type AppVersions struct {
type PrivateLink struct {
AdvertisedHostname string `json:"advertisedHostname"`
}

type ClusterSettings struct {
Description string `json:"description"`
TwoFactorDelete *TwoFactorDelete `json:"twoFactorDelete"`
}
1 change: 1 addition & 0 deletions pkg/models/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ var (
ErrImmutableNodesNumber = errors.New("nodes number is immutable")
ErrMissingSecretKeys = errors.New("the secret is missing the correct keys for the user")
ErrUserStillExist = errors.New("the user is still attached to cluster")
ErrOnlyOneEntityTwoFactorDelete = errors.New("currently only one entity of two factor delete can be filled")
)