github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/tenantfetchersvc/resync/kubernetes.go (about) 1 package resync 2 3 import ( 4 "context" 5 "strconv" 6 "time" 7 8 kube "github.com/kyma-incubator/compass/components/director/pkg/kubernetes" 9 "github.com/kyma-incubator/compass/components/director/pkg/log" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/client-go/kubernetes" 12 ) 13 14 // KubeClient missing godoc 15 //go:generate mockery --name=KubeClient --output=automock --outpkg=automock --case=underscore --disable-version-string 16 type KubeClient interface { 17 GetTenantFetcherConfigMapData(ctx context.Context) (string, string, error) 18 UpdateTenantFetcherConfigMapData(ctx context.Context, lastRunTimestamp, lastResyncTimestamp string) error 19 } 20 21 // NewKubernetesClient missing godoc 22 func NewKubernetesClient(ctx context.Context, cfg KubeConfig) (KubeClient, error) { 23 shouldUseKubernetes, err := strconv.ParseBool(cfg.UseKubernetes) 24 if err != nil { 25 return nil, err 26 } 27 28 if !shouldUseKubernetes { 29 return newNoopKubernetesClient(), nil 30 } 31 return newKubernetesClient(ctx, cfg) 32 } 33 34 type noopKubernetesClient struct { 35 cfg KubeConfig 36 } 37 38 func newNoopKubernetesClient() KubeClient { 39 return &noopKubernetesClient{ 40 cfg: KubeConfig{ 41 ConfigMapNamespace: "namespace", 42 ConfigMapName: "name", 43 ConfigMapTimestampField: "timestampField", 44 }, 45 } 46 } 47 48 // GetTenantFetcherConfigMapData missing godoc 49 func (k *noopKubernetesClient) GetTenantFetcherConfigMapData(_ context.Context) (string, string, error) { 50 return "1", "1", nil 51 } 52 53 // UpdateTenantFetcherConfigMapData missing godoc 54 func (k *noopKubernetesClient) UpdateTenantFetcherConfigMapData(_ context.Context, _, _ string) error { 55 return nil 56 } 57 58 // KubeConfig missing godoc 59 type KubeConfig struct { 60 UseKubernetes string `envconfig:"USE_KUBERNETES"` 61 62 ConfigMapNamespace string `envconfig:"CONFIGMAP_NAMESPACE" default:"compass-system"` 63 ConfigMapName string `envconfig:"LAST_EXECUTION_TIME_CONFIG_MAP_NAME" default:"tenant-fetcher-config"` 64 ConfigMapTimestampField string `envconfig:"CONFIGMAP_TIMESTAMP_FIELD" default:"lastConsumedTenantTimestamp"` 65 ConfigMapResyncTimestampField string `envconfig:"CONFIGMAP_RESYNC_TIMESTAMP_FIELD" default:"lastFullResyncTimestamp"` 66 67 PollInterval time.Duration `envconfig:"APP_KUBERNETES_POLL_INTERVAL" default:"2s"` 68 PollTimeout time.Duration `envconfig:"APP_KUBERNETES_POLL_TIMEOUT" default:"1m"` 69 Timeout time.Duration `envconfig:"APP_KUBERNETES_TIMEOUT" default:"2m"` 70 } 71 72 type kubernetesClient struct { 73 client *kubernetes.Clientset 74 75 cfg KubeConfig 76 } 77 78 func newKubernetesClient(ctx context.Context, cfg KubeConfig) (KubeClient, error) { 79 kubeClientSet, err := kube.NewKubernetesClientSet(ctx, cfg.PollInterval, cfg.PollTimeout, cfg.Timeout) 80 if err != nil { 81 return nil, err 82 } 83 84 return &kubernetesClient{ 85 client: kubeClientSet, 86 cfg: cfg, 87 }, nil 88 } 89 90 // GetTenantFetcherConfigMapData missing godoc 91 func (k *kubernetesClient) GetTenantFetcherConfigMapData(ctx context.Context) (string, string, error) { 92 configMap, err := k.client.CoreV1().ConfigMaps(k.cfg.ConfigMapNamespace).Get(ctx, k.cfg.ConfigMapName, metav1.GetOptions{}) 93 if err != nil { 94 return "", "", err 95 } 96 97 lastRunTimestamp, ok := configMap.Data[k.cfg.ConfigMapTimestampField] 98 if !ok { 99 lastRunTimestamp = "1" 100 } 101 102 lastResyncTimestamp, ok := configMap.Data[k.cfg.ConfigMapResyncTimestampField] 103 if !ok { 104 lastResyncTimestamp = "1" 105 } 106 return lastRunTimestamp, lastResyncTimestamp, nil 107 } 108 109 // UpdateTenantFetcherConfigMapData missing godoc 110 func (k *kubernetesClient) UpdateTenantFetcherConfigMapData(ctx context.Context, lastRunTimestamp, lastResyncTimestamp string) error { 111 configMap, err := k.client.CoreV1().ConfigMaps(k.cfg.ConfigMapNamespace).Get(ctx, k.cfg.ConfigMapName, metav1.GetOptions{}) 112 if err != nil { 113 return err 114 } 115 116 configMap.Data[k.cfg.ConfigMapTimestampField] = lastRunTimestamp 117 configMap.Data[k.cfg.ConfigMapResyncTimestampField] = lastResyncTimestamp 118 _, err = k.client.CoreV1().ConfigMaps(k.cfg.ConfigMapNamespace).Update(ctx, configMap, metav1.UpdateOptions{}) 119 return err 120 } 121 122 func resyncTimestamps(ctx context.Context, client KubeClient, fullResyncInterval time.Duration) (*time.Time, string, string, error) { 123 startTime := time.Now() 124 125 lastConsumedTenantTimestamp, lastFullResyncTimestamp, err := client.GetTenantFetcherConfigMapData(ctx) 126 if err != nil { 127 return nil, "", "", err 128 } 129 130 shouldFullResync, err := shouldFullResync(lastFullResyncTimestamp, fullResyncInterval) 131 if err != nil { 132 return nil, "", "", err 133 } 134 135 if shouldFullResync { 136 log.C(ctx).Infof("Last full resync was %s ago. Will perform a full resync.", fullResyncInterval) 137 lastConsumedTenantTimestamp = "1" 138 lastFullResyncTimestamp = convertTimeToUnixMilliSecondString(startTime) 139 } else { 140 log.C(ctx).Infof("Last full resync was %s ago. Will perform a delta resync.", fullResyncInterval) 141 } 142 return &startTime, lastConsumedTenantTimestamp, lastFullResyncTimestamp, nil 143 } 144 145 func shouldFullResync(lastFullResyncTimestamp string, fullResyncInterval time.Duration) (bool, error) { 146 i, err := strconv.ParseInt(lastFullResyncTimestamp, 10, 64) 147 if err != nil { 148 return false, err 149 } 150 ts := time.Unix(i/1000, 0) 151 return time.Now().After(ts.Add(fullResyncInterval)), nil 152 } 153 154 func convertTimeToUnixMilliSecondString(timestamp time.Time) string { 155 return strconv.FormatInt(timestamp.UnixNano()/int64(time.Millisecond), 10) 156 }