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

allow redis cache to be deployed into a vnet #816

Merged
merged 16 commits into from
Mar 30, 2020
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
8 changes: 5 additions & 3 deletions api/v1alpha1/rediscache_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ type RedisCacheSpec struct {

// RedisCacheProperties the properties of the Redis Cache.
type RedisCacheProperties struct {
Sku RedisCacheSku `json:"sku,omitempty"`

EnableNonSslPort bool `json:"enableNonSslPort,omitempty"`
Sku RedisCacheSku `json:"sku,omitempty"`
EnableNonSslPort bool `json:"enableNonSslPort,omitempty"`
SubnetID string `json:"subnetId,omitempty"`
StaticIP string `json:"staticIp,omitempty"`
Configuration map[string]string `json:"configuration,omitempty"`
}

// RedisCacheSku the SKU of the Redis Cache.
Expand Down
15 changes: 12 additions & 3 deletions config/samples/azure_v1alpha1_rediscache.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ metadata:
spec:
location: westus
resourceGroup: resourcegroup-azure-operators
# Use the field below to optionally specify a different keyvault
# to store the primary and secondary key secrets in
# keyVaultToStoreSecrets: asoSecretKeyVault
properties:
# possible values for sku.Name are "Basic", "Premium" or "Standard"
# possible values for sku.family are "C" and "P".
Expand All @@ -17,6 +20,12 @@ spec:
family: C
capacity: 1
enableNonSslPort: true
# Use the field below to optionally specify a different keyvault
# to store the primary and secondary key secrets in
#keyVaultToStoreSecrets: asoSecretKeyVault
## Optional - vnet usage may require a higher tier sku
frodopwns marked this conversation as resolved.
Show resolved Hide resolved
subnetId: /subscriptions/{SUBID}/resourceGroups/{resourcegroupName}/providers/Microsoft.Network/virtualNetworks/{vnet name}/subnets/{subnet name}
staticIp: 172.22.0.10
# All redis configuration - Few possible keys: rdb-backup-enabled,rdb-storage-connection-string,rdb-backup-frequency,maxmemory-delta,
# maxmemory-policy,notify-keyspace-events,maxmemory-samples,slowlog-log-slower-than,slowlog-max-len,list-max-ziplist-entries,list-max-ziplist-value,
# hash-max-ziplist-entries,hash-max-ziplist-value,set-max-intset-entries,zset-max-ziplist-entries,zset-max-ziplist-value
# configuration:
# key: value

13 changes: 13 additions & 0 deletions pkg/errhelp/errhelp.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package errhelp

import (
"fmt"
"regexp"
"strings"
)

Expand Down Expand Up @@ -41,3 +43,14 @@ func IsStatusCode404(err error) bool {
func IsResourceNotFound(err error) bool {
return strings.Contains(err.Error(), "ResourceNotFound")
}

// StripErrorIDs takes an error and returns its string representation after filtering some common ID patterns
func StripErrorIDs(err error) string {
patterns := []string{
"RequestID=",
"CorrelationId:\\s",
}
reg := regexp.MustCompile(fmt.Sprintf(`(%s)\S+`, strings.Join(patterns, "|")))
return reg.ReplaceAllString(err.Error(), "")

}
7 changes: 1 addition & 6 deletions pkg/resourcemanager/rediscaches/rediscache_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@ import (
type RedisCacheManager interface {
// CreateRedisCache creates a new RedisCache
CreateRedisCache(ctx context.Context,
groupName string,
redisCacheName string,
location string,
sku azurev1alpha1.RedisCacheSku,
enableNonSSLPort bool,
tags map[string]*string) (*redis.ResourceType, error)
instance azurev1alpha1.RedisCache) (*redis.ResourceType, error)

// DeleteRedisCache removes the resource group named by env var
DeleteRedisCache(ctx context.Context, groupName string, redisCacheName string) (result redis.DeleteFuture, err error)
Expand Down
40 changes: 23 additions & 17 deletions pkg/resourcemanager/rediscaches/rediscache_reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,16 @@ func (rc *AzureRedisCacheManager) Ensure(ctx context.Context, obj runtime.Object
return false, err
}

redisName := instance.ObjectMeta.Name
redisName := instance.Name
groupName := instance.Spec.ResourceGroupName
name := instance.ObjectMeta.Name
frodopwns marked this conversation as resolved.
Show resolved Hide resolved
location := instance.Spec.Location
sku := instance.Spec.Properties.Sku
enableNonSSLPort := instance.Spec.Properties.EnableNonSslPort

// convert kube labels to expected tag format
labels := map[string]*string{}
for k, v := range instance.GetLabels() {
value := v
labels[k] = &value
}

if len(instance.Spec.SecretName) == 0 {
instance.Spec.SecretName = redisName
}

instance.Status.Provisioning = true
instance.Status.FailedProvisioning = false

newRc, err := rc.GetRedisCache(ctx, groupName, name)
if err == nil {
Expand All @@ -74,23 +65,38 @@ func (rc *AzureRedisCacheManager) Ensure(ctx context.Context, obj runtime.Object
}
instance.Status.Message = fmt.Sprintf("RedisCache Get error %s", err.Error())

_, err = rc.CreateRedisCache(ctx, groupName, name, location, sku, enableNonSSLPort, labels)
_, err = rc.CreateRedisCache(ctx, *instance)
if err != nil {
instance.Status.Message = err.Error()
instance.Status.Message = errhelp.StripErrorIDs(err)
instance.Status.Provisioning = false
azerr := errhelp.NewAzureErrorAzureError(err)

unrecoverable := []string{
errhelp.RequestConflictError,
errhelp.BadRequest,
}
if helpers.ContainsString(unrecoverable, azerr.Type) || azerr.Code == http.StatusBadRequest {
return true, nil
}

catch := []string{
catchNotProvisioning := []string{
errhelp.ParentNotFoundErrorCode,
errhelp.ResourceGroupNotFoundErrorCode,
errhelp.AlreadyExists,
errhelp.NotFoundErrorCode,
}
if helpers.ContainsString(catchNotProvisioning, azerr.Type) {
return false, nil
}

catchProvisioning := []string{
errhelp.AsyncOpIncompleteError,
errhelp.BadRequest,
}
azerr := errhelp.NewAzureErrorAzureError(err)
if helpers.ContainsString(catch, azerr.Type) {
if helpers.ContainsString(catchProvisioning, azerr.Type) {
instance.Status.Provisioning = true
return false, nil
}

return false, err
}

Expand Down
62 changes: 44 additions & 18 deletions pkg/resourcemanager/rediscaches/rediscaches.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package rediscaches
import (
"context"
"errors"
"fmt"
"log"

"github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis"
Expand Down Expand Up @@ -49,23 +50,28 @@ func getRedisCacheClient() (redis.Client, error) {
}

// CreateRedisCache creates a new RedisCache
func (r *AzureRedisCacheManager) CreateRedisCache(ctx context.Context,
groupName string,
redisCacheName string,
location string,
sku azurev1alpha1.RedisCacheSku,
enableNonSSLPort bool,
tags map[string]*string) (*redis.ResourceType, error) {
redisClient, err := getRedisCacheClient()
func (r *AzureRedisCacheManager) CreateRedisCache(
ctx context.Context,
instance azurev1alpha1.RedisCache) (*redis.ResourceType, error) {

props := instance.Spec.Properties

// convert kube labels to expected tag format
tags := map[string]*string{}
for k, v := range instance.GetLabels() {
value := v
tags[k] = &value
jananivMS marked this conversation as resolved.
Show resolved Hide resolved
}

redisClient, err := getRedisCacheClient()
if err != nil {
return nil, err
}

//Check if name is available
redisType := "Microsoft.Cache/redis"
checkNameParams := redis.CheckNameAvailabilityParameters{
Name: &redisCacheName,
Name: &instance.Name,
Type: &redisType,
}
checkNameResult, err := redisClient.CheckNameAvailability(ctx, checkNameParams)
Expand All @@ -74,27 +80,47 @@ func (r *AzureRedisCacheManager) CreateRedisCache(ctx context.Context,
}

if checkNameResult.StatusCode != 200 {
log.Println("redis cache name (%s) not available: " + redisCacheName + checkNameResult.Status)
log.Println("redis cache name (%s) not available: " + instance.Name + checkNameResult.Status)
return nil, errors.New("redis cache name not available")
}

redisSku := redis.Sku{
Name: redis.SkuName(sku.Name),
Family: redis.SkuFamily(sku.Family),
Capacity: to.Int32Ptr(sku.Capacity),
redisSku := &redis.Sku{
Name: redis.SkuName(props.Sku.Name),
frodopwns marked this conversation as resolved.
Show resolved Hide resolved
Family: redis.SkuFamily(props.Sku.Family),
Capacity: to.Int32Ptr(props.Sku.Capacity),
}

createParams := redis.CreateParameters{
Location: to.StringPtr(location),
Location: to.StringPtr(instance.Spec.Location),
Tags: tags,
CreateProperties: &redis.CreateProperties{
EnableNonSslPort: &enableNonSSLPort,
Sku: &redisSku,
EnableNonSslPort: &props.EnableNonSslPort,
Sku: redisSku,
},
}

// handle vnet settings
if len(props.SubnetID) > 0 {
frodopwns marked this conversation as resolved.
Show resolved Hide resolved
if len(props.StaticIP) == 0 {
return nil, fmt.Errorf("subnet id provided but no static ip has been set")
}
createParams.CreateProperties.SubnetID = &props.SubnetID
createParams.CreateProperties.StaticIP = &props.StaticIP
}

// set redis config if one was provided
if len(props.Configuration) > 0 {
config := map[string]*string{}
for k, v := range props.Configuration {
value := v
config[k] = &value
}
createParams.CreateProperties.RedisConfiguration = config
}

future, err := redisClient.Create(
ctx, groupName, redisCacheName, createParams)
ctx, instance.Spec.ResourceGroupName, instance.Name, createParams,
)
if err != nil {
return nil, err
}
Expand Down