github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/test/testutils/utils.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package testutils
    21  
    22  import (
    23  	"context"
    24  	"io"
    25  	"net/http"
    26  	"sync"
    27  	"time"
    28  
    29  	corev1 "k8s.io/api/core/v1"
    30  	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	apiruntime "k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/apimachinery/pkg/runtime/schema"
    34  	"k8s.io/cli-runtime/pkg/genericclioptions"
    35  	"k8s.io/client-go/dynamic"
    36  	"k8s.io/client-go/kubernetes/scheme"
    37  	"k8s.io/client-go/rest"
    38  	"k8s.io/klog/v2"
    39  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    40  	"sigs.k8s.io/controller-runtime/pkg/client"
    41  
    42  	"github.com/1aal/kubeblocks/pkg/configuration/core"
    43  	viper "github.com/1aal/kubeblocks/pkg/viperx"
    44  )
    45  
    46  const (
    47  	K8sCoreAPIVersion  = "v1"
    48  	ResourceConfigmaps = "configmaps"
    49  )
    50  
    51  // Extensions API group
    52  const (
    53  	ExtensionsAPIGroup   = "extensions.kubeblocks.io"
    54  	ExtensionsAPIVersion = "v1alpha1"
    55  	ResourceAddons       = "addons"
    56  )
    57  
    58  // The purpose of this package is to remove the dependency of the KubeBlocks project on 'pkg/cli'.
    59  var (
    60  	// KubeBlocksRepoName helm repo name for kubeblocks
    61  	KubeBlocksRepoName = "kubeblocks"
    62  
    63  	// KubeBlocksChartName helm chart name for kubeblocks
    64  	KubeBlocksChartName = "kubeblocks"
    65  
    66  	// KubeBlocksReleaseName helm release name for kubeblocks
    67  	KubeBlocksReleaseName = "kubeblocks"
    68  
    69  	// KubeBlocksChartURL the helm chart repo for installing kubeblocks
    70  	KubeBlocksChartURL = "https://apecloud.github.io/helm-charts"
    71  
    72  	// GitLabHelmChartRepo the helm chart repo in GitLab
    73  	GitLabHelmChartRepo = "https://jihulab.com/api/v4/projects/85949/packages/helm/stable"
    74  
    75  	// CfgKeyHelmRepoURL the helm repo url key
    76  	CfgKeyHelmRepoURL = "HELM_REPO_URL"
    77  )
    78  
    79  func ConfigmapGVR() schema.GroupVersionResource {
    80  	return schema.GroupVersionResource{Group: corev1.GroupName, Version: K8sCoreAPIVersion, Resource: ResourceConfigmaps}
    81  }
    82  
    83  func AddonGVR() schema.GroupVersionResource {
    84  	return schema.GroupVersionResource{Group: ExtensionsAPIGroup, Version: ExtensionsAPIVersion, Resource: ResourceAddons}
    85  }
    86  
    87  var addToScheme sync.Once
    88  
    89  func NewFactory() cmdutil.Factory {
    90  	configFlags := NewConfigFlagNoWarnings()
    91  	// Add CRDs to the scheme. They are missing by default.
    92  	addToScheme.Do(func() {
    93  		if err := apiextv1.AddToScheme(scheme.Scheme); err != nil {
    94  			// This should never happen.
    95  			panic(err)
    96  		}
    97  	})
    98  	return cmdutil.NewFactory(configFlags)
    99  }
   100  
   101  // NewConfigFlagNoWarnings returns a ConfigFlags that disables warnings.
   102  func NewConfigFlagNoWarnings() *genericclioptions.ConfigFlags {
   103  	configFlags := genericclioptions.NewConfigFlags(true)
   104  	configFlags.WrapConfigFn = func(c *rest.Config) *rest.Config {
   105  		c.WarningHandler = rest.NoWarnings{}
   106  		return c
   107  	}
   108  	return configFlags
   109  }
   110  
   111  // GetResourceObjectFromGVR queries the resource object using GVR.
   112  func GetResourceObjectFromGVR(gvr schema.GroupVersionResource, key client.ObjectKey, client dynamic.Interface, k8sObj interface{}) error {
   113  	unstructuredObj, err := client.
   114  		Resource(gvr).
   115  		Namespace(key.Namespace).
   116  		Get(context.TODO(), key.Name, metav1.GetOptions{})
   117  	if err != nil {
   118  		return core.WrapError(err, "failed to get resource[%v]", key)
   119  	}
   120  	return apiruntime.DefaultUnstructuredConverter.FromUnstructured(unstructuredObj.Object, k8sObj)
   121  }
   122  
   123  // GetHelmChartRepoURL gets helm chart repo, chooses one from GitHub and GitLab based on the IP location
   124  func GetHelmChartRepoURL() string {
   125  
   126  	// if helm repo url is specified by config or environment, use it
   127  	url := viper.GetString(CfgKeyHelmRepoURL)
   128  	if url != "" {
   129  		klog.V(1).Infof("Using helm repo url set by config or environment: %s", url)
   130  		return url
   131  	}
   132  
   133  	// if helm repo url is not specified, choose one from GitHub and GitLab based on the IP location
   134  	// if location is CN, or we can not get location, use GitLab helm chart repo
   135  	repo := KubeBlocksChartURL
   136  	location, _ := GetIPLocation()
   137  	if location == "CN" || location == "" {
   138  		repo = GitLabHelmChartRepo
   139  	}
   140  	klog.V(1).Infof("Using helm repo url: %s", repo)
   141  	return repo
   142  }
   143  
   144  func GetIPLocation() (string, error) {
   145  	client := &http.Client{Timeout: 10 * time.Second}
   146  	req, err := http.NewRequest("GET", "https://ifconfig.io/country_code", nil)
   147  	if err != nil {
   148  		return "", err
   149  	}
   150  	resp, err := client.Do(req)
   151  	if err != nil {
   152  		return "", err
   153  	}
   154  	defer resp.Body.Close()
   155  	location, err := io.ReadAll(resp.Body)
   156  	if len(location) == 0 || err != nil {
   157  		return "", err
   158  	}
   159  
   160  	// remove last "\n"
   161  	return string(location[:len(location)-1]), nil
   162  }