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

feat(AzureRedisInstance): add KCP reconciler - load, create, delete #369

Merged
merged 13 commits into from
Jul 29, 2024
67 changes: 42 additions & 25 deletions api/cloud-control/v1beta1/redisinstance_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ limitations under the License.
package v1beta1

import (
armRedis "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

const (
ReasonCanNotLoadResourceGroup = "ResourceGroupCanNotLoad"
ReasonCanNotDeleteResourceGroup = "ResourceGroupCanNotDelete"

ReasonCanNotCreateResourceGroup = "ResourceGroupCanNotCreate"
)

// RedisInstanceSpec defines the desired state of RedisInstance
type RedisInstanceSpec struct {
// +kubebuilder:validation:Required
Expand Down Expand Up @@ -71,16 +79,6 @@ type RedisInstanceGcpConfigs struct {
}

type RedisInstanceAzureConfigs struct {
// +optional
AadEnabled string `json:"aad-enabled,omitempty"`
// +optional
AofBackupEnabled string `json:"aof-backup-enabled,omitempty"`
// +optional
AofStorageConnectionString0 string `json:"aof-storage-connection-string-0,omitempty"`
// +optional
AofStorageConnectionString1 string `json:"aof-storage-connection-string-1,omitempty"`
// +optional
AuthNotRequired string `json:"authnotrequired,omitempty"`
// +optional
MaxClients string `json:"maxclients,omitempty"`
// +optional
Expand All @@ -94,24 +92,43 @@ type RedisInstanceAzureConfigs struct {
// +optional
NotifyKeyspaceEvents string `json:"notify-keyspace-events,omitempty"`
// +optional
PreferredDataArchiveAuthMethod string `json:"preferred-data-archive-auth-method,omitempty"`
// +optional
PreferredDataPersistenceAuthMethod string `json:"preferred-data-persistence-auth-method,omitempty"`
// +optional
RdbBackupEnabled string `json:"rdb-backup-enabled,omitempty"`
// +optional
// +kubebuilder:validation:Enum=15;30;60;360;720;1440
RdbBackupFrequency string `json:"rdb-backup-frequency,omitempty"`
// +optional
RdbBackupMaxSnapshotCount string `json:"rdb-backup-max-snapshot-count,omitempty"`
// +optional
RdbStorageConnectionString string `json:"rdb-storage-connection-string,omitempty"`
// +optional
StorageSubscriptionId string `json:"storage-subscription-id,omitempty"`
// +optional
ZonalConfiguration string `json:"zonal-configuration,omitempty"`
}

func (redisConfigs *RedisInstanceAzureConfigs) GetRedisConfig() *armRedis.CommonPropertiesRedisConfiguration {
redisConfiguration := armRedis.CommonPropertiesRedisConfiguration{}

additionalProperties := map[string]interface{}{}

if redisConfigs.MaxFragmentationMemoryReserved != "" {
redisConfiguration.MaxfragmentationmemoryReserved = &redisConfigs.MaxFragmentationMemoryReserved
}
if redisConfigs.MaxMemoryDelta != "" {
redisConfiguration.MaxmemoryDelta = &redisConfigs.MaxMemoryDelta
}
if redisConfigs.MaxMemoryPolicy != "" {
redisConfiguration.MaxmemoryPolicy = &redisConfigs.MaxMemoryPolicy
}
if redisConfigs.MaxMemoryReserved != "" {
redisConfiguration.MaxmemoryReserved = &redisConfigs.MaxMemoryReserved
}
if redisConfigs.NotifyKeyspaceEvents != "" {
additionalProperties["notify-keyspace-events"] = &redisConfigs.NotifyKeyspaceEvents
}
if redisConfigs.MaxClients != "" {
redisConfiguration.Maxclients = &redisConfigs.MaxClients
}
if redisConfigs.ZonalConfiguration != "" {
redisConfiguration.ZonalConfiguration = &redisConfigs.ZonalConfiguration
}

if len(additionalProperties) > 0 {
redisConfiguration.AdditionalProperties = additionalProperties
}

return &redisConfiguration
}

type AzureRedisSKU struct {
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=1;2;3;4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,6 @@ spec:
type: boolean
redisConfiguration:
properties:
aad-enabled:
type: string
aof-backup-enabled:
type: string
aof-storage-connection-string-0:
type: string
aof-storage-connection-string-1:
type: string
authnotrequired:
type: string
maxclients:
type: string
maxfragmentationmemory-reserved:
Expand All @@ -88,27 +78,6 @@ spec:
type: string
notify-keyspace-events:
type: string
preferred-data-archive-auth-method:
type: string
preferred-data-persistence-auth-method:
type: string
rdb-backup-enabled:
type: string
rdb-backup-frequency:
enum:
- 15
- 30
- 60
- 360
- 720
- 1440
type: string
rdb-backup-max-snapshot-count:
type: string
rdb-storage-connection-string:
type: string
storage-subscription-id:
type: string
zonal-configuration:
type: string
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,6 @@ spec:
type: boolean
redisConfiguration:
properties:
aad-enabled:
type: string
aof-backup-enabled:
type: string
aof-storage-connection-string-0:
type: string
aof-storage-connection-string-1:
type: string
authnotrequired:
type: string
maxclients:
type: string
maxfragmentationmemory-reserved:
Expand All @@ -88,27 +78,6 @@ spec:
type: string
notify-keyspace-events:
type: string
preferred-data-archive-auth-method:
type: string
preferred-data-persistence-auth-method:
type: string
rdb-backup-enabled:
type: string
rdb-backup-frequency:
enum:
- 15
- 30
- 60
- 360
- 720
- 1440
type: string
rdb-backup-max-snapshot-count:
type: string
rdb-storage-connection-string:
type: string
storage-subscription-id:
type: string
zonal-configuration:
type: string
type: object
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v5 v5.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis v1.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0
github.com/aws/aws-sdk-go-v2 v1.30.3
github.com/aws/aws-sdk-go-v2/config v1.27.27
github.com/aws/aws-sdk-go-v2/credentials v1.17.27
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.0.0 h1:Kb8eVvjdP6kZqYnER5w/PiGCFp91yVgaxve3d7kCEpY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.0.0/go.mod h1:lYq15QkJyEsNegz5EhI/0SXQ6spvGfgwBH/Qyzkoc/s=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v5 v5.2.0 h1:qBlqTo40ARdI7Pmq+enBiTnejZk2BF+PHgktgG8k3r8=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v5 v5.2.0/go.mod h1:UmyOatRyQodVpp55Jr5WJmnkmVW4wKfo85uHFmMEjfM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis v1.0.0 h1:nmpTBgRg1HynngFYICRhceC7s5dmbKN9fJ/XQz/UQ2I=
Expand Down
116 changes: 116 additions & 0 deletions internal/controller/cloud-control/redisinstance_azure_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package cloudcontrol

import (
azureUtil "github.com/kyma-project/cloud-manager/pkg/kcp/provider/azure/util"
"time"

cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1"
iprangePkg "github.com/kyma-project/cloud-manager/pkg/kcp/iprange"
scopePkg "github.com/kyma-project/cloud-manager/pkg/kcp/scope"
. "github.com/kyma-project/cloud-manager/pkg/testinfra/dsl"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Feature: KCP RedisInstance", func() {

It("Scenario: KCP Azure RedisInstance is created and deleted", func() {

name := "924a92cf-9e72-408d-a1e8-017a2fd8d42e"
scope := &cloudcontrolv1beta1.Scope{}

By("Given Scope exists", func() {
// Tell Scope reconciler to ignore this kymaName
scopePkg.Ignore.AddName(name)

Eventually(CreateScopeAzure).
WithArguments(infra.Ctx(), infra, scope, WithName(name)).
Should(Succeed())
})

kcpIpRangeName := "ffc7ebcc-114e-4d68-948c-241405fd01b6"
kcpIpRange := &cloudcontrolv1beta1.IpRange{}

// Tell IpRange reconciler to ignore this kymaName
iprangePkg.Ignore.AddName(kcpIpRangeName)
By("And Given KCP IPRange exists", func() {
Eventually(CreateKcpIpRange).
WithArguments(
infra.Ctx(), infra.KCP().Client(), kcpIpRange,
WithName(kcpIpRangeName),
WithKcpIpRangeSpecScope(scope.Name),
).
Should(Succeed())
})

By("And Given KCP IpRange has Ready condition", func() {
Eventually(UpdateStatus).
WithArguments(
infra.Ctx(), infra.KCP().Client(), kcpIpRange,
WithKcpIpRangeStatusCidr(kcpIpRange.Spec.Cidr),
WithConditions(KcpReadyCondition()),
).WithTimeout(20*time.Second).WithPolling(200*time.Millisecond).
Should(Succeed(), "Expected KCP IpRange to become ready")
})

redisInstance := &cloudcontrolv1beta1.RedisInstance{}

By("When RedisInstance is created", func() {
Eventually(CreateRedisInstance).
WithArguments(infra.Ctx(), infra.KCP().Client(), redisInstance,
WithName(name),
WithRemoteRef("skr-redis-example"),
WithIpRange(kcpIpRangeName),
WithInstanceScope(name),
WithRedisInstanceAzure(),
WithSKU(2),
WithKcpAzureRedisVersion("6.0"),
).
Should(Succeed(), "failed creating RedisInstance")
})

By("Then Azure Redis is created", func() {
Eventually(LoadAndCheck).
WithArguments(infra.Ctx(), infra.KCP().Client(), redisInstance,
NewObjActions(),
HavingRedisInstanceStatusId()).
Should(Succeed(), "expected RedisInstance to get status.id")
})

By("Then RedisInstance has Ready condition", func() {
Eventually(LoadAndCheck).
WithArguments(infra.Ctx(), infra.KCP().Client(), redisInstance,
NewObjActions(),
HavingConditionTrue(cloudcontrolv1beta1.ConditionTypeReady),
).
Should(Succeed(), "expected RedisInstance to has Ready state, but it didn't")
})

By("And Then RedisInstance has .status.primaryEndpoint set", func() {
Expect(len(redisInstance.Status.PrimaryEndpoint) > 0).To(Equal(true))
})
By("And Then RedisInstance has .status.authString set", func() {
Expect(len(redisInstance.Status.AuthString) > 0).To(Equal(true))
})

// DELETE

By("When RedisInstance is deleted", func() {
Eventually(Delete).
WithArguments(infra.Ctx(), infra.KCP().Client(), redisInstance).
Should(Succeed(), "failed deleting RedisInstance")
})

By("And When Azure Redis state is deleted", func() {
rgName := azureUtil.GetResourceGroupName("redis", redisInstance.Name)
infra.AzureMock().DeleteRedisCacheByResourceGroupName(rgName)
})

By("Then RedisInstance does not exist", func() {
Eventually(IsDeleted, 5*time.Second).
WithArguments(infra.Ctx(), infra.KCP().Client(), redisInstance).
Should(Succeed(), "expected RedisInstance not to exist (be deleted), but it still exists")
})
})

})
9 changes: 8 additions & 1 deletion pkg/kcp/provider/azure/meta/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ func TooManyRequests(err error) bool {
func IsNotFound(err error) bool {
var respErr *azcore.ResponseError

return errors.As(err, &respErr) && respErr.ErrorCode == "ResourceNotFound"
if ok := errors.As(err, &respErr); ok {
if respErr.StatusCode == 404 {
return true
}
return respErr.ErrorCode == "ResourceNotFound"
}

return false
}

func ErrorToRequeueResponse(err error) error {
Expand Down
Loading
Loading