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  }