github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/util/util.go (about)

     1  /*
     2   * Copyright contributors to the Hyperledger Fabric Operator project
     3   *
     4   * SPDX-License-Identifier: Apache-2.0
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at:
     9   *
    10   * 	  http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  package util
    20  
    21  import (
    22  	"context"
    23  	"crypto/rand"
    24  	"crypto/tls"
    25  	"crypto/x509"
    26  	"encoding/base64"
    27  	"encoding/json"
    28  	"encoding/pem"
    29  	"fmt"
    30  	"io/ioutil"
    31  	"math/big"
    32  	"net"
    33  	"net/http"
    34  	"net/url"
    35  	"os"
    36  	"path"
    37  	"path/filepath"
    38  	"strings"
    39  	"time"
    40  
    41  	"github.com/IBM-Blockchain/fabric-operator/pkg/k8s/clientset"
    42  	routev1 "github.com/openshift/api/route/v1"
    43  	"github.com/pkg/errors"
    44  	appsv1 "k8s.io/api/apps/v1"
    45  	corev1 "k8s.io/api/core/v1"
    46  	networkingv1 "k8s.io/api/networking/v1"
    47  	networkingv1beta1 "k8s.io/api/networking/v1beta1"
    48  	rbacv1 "k8s.io/api/rbac/v1"
    49  	extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    50  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    51  	"k8s.io/apimachinery/pkg/types"
    52  	"k8s.io/apimachinery/pkg/util/strategicpatch"
    53  	"k8s.io/apimachinery/pkg/util/yaml"
    54  	"k8s.io/apimachinery/pkg/version"
    55  	"k8s.io/client-go/rest"
    56  	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
    57  	yaml1 "sigs.k8s.io/yaml"
    58  )
    59  
    60  const (
    61  	maximumCRNameLength = 32
    62  )
    63  
    64  func ConvertYamlFileToJson(file string) ([]byte, error) {
    65  	absfilepath, err := filepath.Abs(file)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	bytes, err := ioutil.ReadFile(filepath.Clean(absfilepath))
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	return yaml.ToJSON(bytes)
    75  }
    76  
    77  func GetContainerFromFile(file string) (*corev1.Container, error) {
    78  	jsonBytes, err := ConvertYamlFileToJson(file)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	cont := &corev1.Container{}
    84  	err = json.Unmarshal(jsonBytes, &cont)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	return cont, nil
    90  }
    91  
    92  func GetPVCFromFile(file string) (*corev1.PersistentVolumeClaim, error) {
    93  	jsonBytes, err := ConvertYamlFileToJson(file)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	pvc := &corev1.PersistentVolumeClaim{}
    99  	err = json.Unmarshal(jsonBytes, &pvc)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	return pvc, nil
   105  }
   106  
   107  func GetRoleFromFile(file string) (*rbacv1.Role, error) {
   108  	jsonBytes, err := ConvertYamlFileToJson(file)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	role := &rbacv1.Role{}
   114  	err = json.Unmarshal(jsonBytes, &role)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	return role, nil
   120  }
   121  
   122  func GetClusterRoleFromFile(file string) (*rbacv1.ClusterRole, error) {
   123  	jsonBytes, err := ConvertYamlFileToJson(file)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	role := &rbacv1.ClusterRole{}
   129  	err = json.Unmarshal(jsonBytes, &role)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	return role, nil
   135  }
   136  
   137  func GetRoleBindingFromFile(file string) (*rbacv1.RoleBinding, error) {
   138  	jsonBytes, err := ConvertYamlFileToJson(file)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	rolebinding := &rbacv1.RoleBinding{}
   144  	err = json.Unmarshal(jsonBytes, &rolebinding)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	return rolebinding, nil
   150  }
   151  
   152  func GetClusterRoleBindingFromFile(file string) (*rbacv1.ClusterRoleBinding, error) {
   153  	jsonBytes, err := ConvertYamlFileToJson(file)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	rolebinding := &rbacv1.ClusterRoleBinding{}
   159  	err = json.Unmarshal(jsonBytes, &rolebinding)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	return rolebinding, nil
   165  }
   166  
   167  func GetServiceAccountFromFile(file string) (*corev1.ServiceAccount, error) {
   168  	jsonBytes, err := ConvertYamlFileToJson(file)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	serviceaccount := &corev1.ServiceAccount{}
   174  	err = json.Unmarshal(jsonBytes, &serviceaccount)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	return serviceaccount, nil
   180  }
   181  
   182  func GetDeploymentFromFile(file string) (*appsv1.Deployment, error) {
   183  	jsonBytes, err := ConvertYamlFileToJson(file)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	dep := &appsv1.Deployment{}
   189  	err = json.Unmarshal(jsonBytes, &dep)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	return dep, nil
   195  }
   196  
   197  func GetServiceFromFile(file string) (*corev1.Service, error) {
   198  	jsonBytes, err := ConvertYamlFileToJson(file)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	svc := &corev1.Service{}
   204  	err = json.Unmarshal(jsonBytes, &svc)
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	return svc, nil
   210  }
   211  
   212  func GetConfigMapFromFile(file string) (*corev1.ConfigMap, error) {
   213  	absfilepath, err := filepath.Abs(file)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  	bytes, err := ioutil.ReadFile(filepath.Clean(absfilepath))
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	cm := &corev1.ConfigMap{}
   222  	err = yaml1.Unmarshal(bytes, cm)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  
   227  	return cm, nil
   228  }
   229  
   230  func GetRouteFromFile(file string) (*routev1.Route, error) {
   231  	jsonBytes, err := ConvertYamlFileToJson(file)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	route := &routev1.Route{}
   237  	err = json.Unmarshal(jsonBytes, &route)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	return route, nil
   243  }
   244  
   245  func GetIngressFromFile(file string) (*networkingv1.Ingress, error) {
   246  	jsonBytes, err := ConvertYamlFileToJson(file)
   247  	if err != nil {
   248  		return nil, err
   249  	}
   250  
   251  	ingress := &networkingv1.Ingress{}
   252  	err = json.Unmarshal(jsonBytes, &ingress)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  
   257  	return ingress, nil
   258  }
   259  
   260  func GetIngressv1beta1FromFile(file string) (*networkingv1beta1.Ingress, error) {
   261  	jsonBytes, err := ConvertYamlFileToJson(file)
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  
   266  	ingress := &networkingv1beta1.Ingress{}
   267  	err = json.Unmarshal(jsonBytes, &ingress)
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  
   272  	return ingress, nil
   273  }
   274  
   275  func GetSecretFromFile(file string) (*corev1.Secret, error) {
   276  	jsonBytes, err := ConvertYamlFileToJson(file)
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  
   281  	secret := &corev1.Secret{}
   282  	err = json.Unmarshal(jsonBytes, &secret)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  
   287  	return secret, nil
   288  }
   289  
   290  func GetCRDFromFile(file string) (*extv1.CustomResourceDefinition, error) {
   291  	jsonBytes, err := ConvertYamlFileToJson(file)
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  
   296  	crd := &extv1.CustomResourceDefinition{}
   297  	err = json.Unmarshal(jsonBytes, &crd)
   298  	if err != nil {
   299  		return nil, err
   300  	}
   301  
   302  	return crd, nil
   303  }
   304  
   305  func GetPodFromFile(file string) (*corev1.Pod, error) {
   306  	jsonBytes, err := ConvertYamlFileToJson(file)
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	pod := &corev1.Pod{}
   312  	err = json.Unmarshal(jsonBytes, &pod)
   313  	if err != nil {
   314  		return nil, err
   315  	}
   316  
   317  	return pod, nil
   318  }
   319  
   320  func GetResourcePatch(current, new *corev1.ResourceRequirements) (*corev1.ResourceRequirements, error) {
   321  	currentBytes, err := json.Marshal(current)
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  
   326  	newBytes, err := json.Marshal(new)
   327  	if err != nil {
   328  		return nil, err
   329  	}
   330  
   331  	patchBytes, err := strategicpatch.StrategicMergePatch(currentBytes, newBytes, corev1.ResourceRequirements{})
   332  	if err != nil {
   333  		return nil, err
   334  	}
   335  
   336  	update := &corev1.ResourceRequirements{}
   337  	err = json.Unmarshal(patchBytes, update)
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  
   342  	return update, nil
   343  }
   344  
   345  func IgnoreAlreadyExistError(err error) error {
   346  	if !strings.Contains(err.Error(), "already exists") {
   347  		return err
   348  	}
   349  	return nil
   350  }
   351  
   352  // Ignore benign error
   353  func IgnoreOutdatedResourceVersion(err error) error {
   354  	if err == nil {
   355  		return nil
   356  	}
   357  
   358  	if !strings.Contains(err.Error(), "please apply your changes to the latest version and try again") {
   359  		return err
   360  	}
   361  
   362  	return nil
   363  }
   364  
   365  func EnvExists(envs []corev1.EnvVar, key string) bool {
   366  	for _, ele := range envs {
   367  		if ele.Name == key {
   368  			return true
   369  		}
   370  	}
   371  	return false
   372  }
   373  
   374  func GetEnvValue(envs []corev1.EnvVar, key string) string {
   375  	for _, ele := range envs {
   376  		if ele.Name == key {
   377  			return ele.Value
   378  		}
   379  	}
   380  	return ""
   381  }
   382  
   383  func ReplaceEnvIfDiff(envs []corev1.EnvVar, key, replace string) ([]corev1.EnvVar, bool) {
   384  	var updated bool
   385  	for _, ele := range envs {
   386  		if ele.Name == key {
   387  			oldValue := ele.Value
   388  			if oldValue != replace {
   389  				envs = UpdateEnvVar(ele.Name, replace, envs)
   390  				updated = true
   391  			}
   392  		}
   393  	}
   394  	return envs, updated
   395  }
   396  
   397  func AppendStringIfMissing(array []string, newEle string) []string {
   398  	for _, ele := range array {
   399  		if ele == newEle {
   400  			return array
   401  		}
   402  	}
   403  	return append(array, newEle)
   404  }
   405  
   406  func AppendEnvIfMissing(envs []corev1.EnvVar, env corev1.EnvVar) []corev1.EnvVar {
   407  	for _, ele := range envs {
   408  		if ele.Name == env.Name {
   409  			return envs
   410  		}
   411  	}
   412  	return append(envs, env)
   413  }
   414  
   415  func AppendPullSecretIfMissing(pullSecrets []corev1.LocalObjectReference, pullSecret string) []corev1.LocalObjectReference {
   416  	for _, ps := range pullSecrets {
   417  		if ps.Name == pullSecret {
   418  			return pullSecrets
   419  		}
   420  	}
   421  	return append(pullSecrets, corev1.LocalObjectReference{Name: pullSecret})
   422  }
   423  
   424  func AppendEnvIfMissingOverrideIfPresent(envs []corev1.EnvVar, env corev1.EnvVar) []corev1.EnvVar {
   425  	for index, ele := range envs {
   426  		if ele.Name == env.Name {
   427  			ele.Value = env.Value
   428  			envs[index] = ele
   429  			return envs
   430  		}
   431  	}
   432  	return append(envs, env)
   433  }
   434  
   435  func AppendConfigMapFromSourceIfMissing(envFroms []corev1.EnvFromSource, envFrom corev1.EnvFromSource) []corev1.EnvFromSource {
   436  	for _, ele := range envFroms {
   437  		if ele.ConfigMapRef.Name == envFrom.ConfigMapRef.Name {
   438  			return envFroms
   439  		}
   440  	}
   441  	return append(envFroms, envFrom)
   442  }
   443  
   444  func AppendVolumeIfMissing(volumes []corev1.Volume, volume corev1.Volume) []corev1.Volume {
   445  	for _, v := range volumes {
   446  		if v.Name == volume.Name {
   447  			return volumes
   448  		}
   449  	}
   450  	return append(volumes, volume)
   451  }
   452  
   453  func AppendVolumeMountIfMissing(volumeMounts []corev1.VolumeMount, volumeMount corev1.VolumeMount) []corev1.VolumeMount {
   454  	for _, v := range volumeMounts {
   455  		if v.Name == volumeMount.Name {
   456  			if v.MountPath == volumeMount.MountPath {
   457  				return volumeMounts
   458  			}
   459  		}
   460  	}
   461  	return append(volumeMounts, volumeMount)
   462  }
   463  
   464  func AppendVolumeMountWithSubPathIfMissing(volumeMounts []corev1.VolumeMount, volumeMount corev1.VolumeMount) []corev1.VolumeMount {
   465  	for _, v := range volumeMounts {
   466  		if v.Name == volumeMount.Name {
   467  			if v.SubPath == volumeMount.SubPath {
   468  				return volumeMounts
   469  			}
   470  		}
   471  	}
   472  	return append(volumeMounts, volumeMount)
   473  }
   474  
   475  func AppendContainerIfMissing(containers []corev1.Container, container corev1.Container) []corev1.Container {
   476  	for _, c := range containers {
   477  		if c.Name == container.Name {
   478  			return containers
   479  		}
   480  	}
   481  	return append(containers, container)
   482  }
   483  
   484  func AppendImagePullSecretIfMissing(imagePullSecrets []corev1.LocalObjectReference, imagePullSecret corev1.LocalObjectReference) []corev1.LocalObjectReference {
   485  	if imagePullSecret.Name == "" {
   486  		return imagePullSecrets
   487  	}
   488  	for _, i := range imagePullSecrets {
   489  		if i.Name == imagePullSecret.Name {
   490  			return imagePullSecrets
   491  		}
   492  	}
   493  	return append(imagePullSecrets, imagePullSecret)
   494  }
   495  
   496  func UpdateEnvVar(name string, value string, envs []corev1.EnvVar) []corev1.EnvVar {
   497  	newEnvs := []corev1.EnvVar{}
   498  	for _, e := range envs {
   499  		if e.Name == name {
   500  			e.Value = value
   501  		}
   502  		newEnvs = append(newEnvs, e)
   503  	}
   504  	return newEnvs
   505  }
   506  
   507  func ValidationChecks(typedata metav1.TypeMeta, metadata metav1.ObjectMeta, expectedKind string, maxNameLength *int) error {
   508  	maxlength := maximumCRNameLength
   509  
   510  	if maxNameLength != nil {
   511  		maxlength = *maxNameLength
   512  	}
   513  
   514  	if len(metadata.Name) > maxlength {
   515  		return fmt.Errorf("The instance name '%s' is too long, the name must be less than or equal to %d characters", metadata.Name, maxlength)
   516  	}
   517  
   518  	if typedata.Kind != "" {
   519  		if typedata.Kind != expectedKind {
   520  			return fmt.Errorf("The instance '%s' is of kind %s not an %s kind resource, please check to make sure there are no name collisions across resources", metadata.Name, typedata.Kind, expectedKind)
   521  		}
   522  	}
   523  
   524  	return nil
   525  }
   526  
   527  func SelectRandomValue(values []string) string {
   528  	if len(values) == 0 {
   529  		return ""
   530  	}
   531  	randValue, _ := rand.Int(rand.Reader, big.NewInt(int64(len(values))))
   532  	return values[randValue.Int64()]
   533  }
   534  
   535  type Client interface {
   536  	Get(ctx context.Context, namespacedName types.NamespacedName, obj k8sclient.Object) error
   537  	List(ctx context.Context, list k8sclient.ObjectList, opts ...k8sclient.ListOption) error
   538  }
   539  
   540  func GetZone(client Client) string {
   541  	nodeList := &corev1.NodeList{}
   542  	err := client.List(context.TODO(), nodeList)
   543  	if err != nil {
   544  		return ""
   545  	}
   546  
   547  	zones := []string{}
   548  	for _, node := range nodeList.Items {
   549  		zone := node.ObjectMeta.Labels["topology.kubernetes.io/zone"]
   550  		zones = append(zones, zone)
   551  	}
   552  
   553  	return SelectRandomValue(zones)
   554  }
   555  
   556  func GetRegion(client Client) string {
   557  	nodeList := &corev1.NodeList{}
   558  	err := client.List(context.TODO(), nodeList)
   559  	if err != nil {
   560  		return ""
   561  	}
   562  
   563  	regions := []string{}
   564  	for _, node := range nodeList.Items {
   565  		region := node.ObjectMeta.Labels["topology.kubernetes.io/region"]
   566  		regions = append(regions, region)
   567  	}
   568  
   569  	return SelectRandomValue(regions)
   570  }
   571  
   572  func ContainsValue(find string, in []string) bool {
   573  	for _, value := range in {
   574  		if find == value {
   575  			return true
   576  		}
   577  	}
   578  	return false
   579  }
   580  
   581  func ValidateZone(client Client, requestedZone string) error {
   582  	nodeList := &corev1.NodeList{}
   583  	err := client.List(context.TODO(), nodeList)
   584  	if err != nil {
   585  		return nil
   586  	}
   587  	zones := []string{}
   588  	for _, node := range nodeList.Items {
   589  		zone := node.ObjectMeta.Labels["topology.kubernetes.io/zone"]
   590  		zones = append(zones, zone)
   591  		zone = node.ObjectMeta.Labels["failure-domain.beta.kubernetes.io/zone"]
   592  		zones = append(zones, zone)
   593  		zone = node.ObjectMeta.Labels["ibm-cloud.kubernetes.io/zone"]
   594  		zones = append(zones, zone)
   595  	}
   596  	valueFound := ContainsValue(requestedZone, zones)
   597  	if !valueFound {
   598  		return errors.Errorf("Zone '%s' is not a valid zone", requestedZone)
   599  	}
   600  	return nil
   601  }
   602  
   603  func ValidateRegion(client Client, requestedRegion string) error {
   604  	nodeList := &corev1.NodeList{}
   605  	err := client.List(context.TODO(), nodeList)
   606  	if err != nil {
   607  		return nil
   608  	}
   609  	regions := []string{}
   610  	for _, node := range nodeList.Items {
   611  		region := node.ObjectMeta.Labels["topology.kubernetes.io/region"]
   612  		regions = append(regions, region)
   613  		region = node.ObjectMeta.Labels["failure-domain.beta.kubernetes.io/region"]
   614  		regions = append(regions, region)
   615  		region = node.ObjectMeta.Labels["ibm-cloud.kubernetes.io/region"]
   616  		regions = append(regions, region)
   617  	}
   618  	valueFound := ContainsValue(requestedRegion, regions)
   619  	if !valueFound {
   620  		return errors.Errorf("Region '%s' is not a valid region", requestedRegion)
   621  	}
   622  	return nil
   623  }
   624  
   625  func FileExists(path string) bool {
   626  	if _, err := os.Stat(path); err == nil {
   627  		return true
   628  	}
   629  	return false
   630  }
   631  
   632  func EnsureDir(dirName string) error {
   633  	err := os.MkdirAll(dirName, 0750)
   634  
   635  	if err == nil || os.IsExist(err) {
   636  		return nil
   637  	} else {
   638  		return err
   639  	}
   640  }
   641  
   642  func GetResourceVerFromSecret(client Client, name, namespace string) (string, error) {
   643  	secret := &corev1.Secret{}
   644  	err := client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, secret)
   645  	if err != nil {
   646  		return "", err
   647  	}
   648  
   649  	resourceVer := secret.ObjectMeta.ResourceVersion
   650  	return resourceVer, nil
   651  }
   652  
   653  func JoinMaps(m1, m2 map[string][]byte) map[string][]byte {
   654  	joined := map[string][]byte{}
   655  
   656  	if m1 != nil {
   657  		for k, v := range m1 {
   658  			joined[k] = v
   659  		}
   660  	}
   661  
   662  	if m2 != nil {
   663  		for k, v := range m2 {
   664  			joined[k] = v
   665  		}
   666  	}
   667  
   668  	return joined
   669  }
   670  
   671  func PemStringToBytes(pem string) []byte {
   672  	return []byte(pem)
   673  }
   674  
   675  func FileToBytes(file string) ([]byte, error) {
   676  	data, err := ioutil.ReadFile(filepath.Clean(file))
   677  	if err != nil {
   678  		return nil, errors.Wrapf(err, "failed to read file %s", file)
   679  	}
   680  
   681  	return data, nil
   682  }
   683  
   684  func Base64ToBytes(base64str string) ([]byte, error) {
   685  	data, err := base64.StdEncoding.DecodeString(base64str)
   686  	if err != nil {
   687  		// If base64 encoded string is padded with too many '=' at the
   688  		// end DecodeString will fail with error: "illegal base64 data at input byte ...".
   689  		// Need to try stripping of '=' at the one at a time and trying again until no more '='
   690  		// left at that point return err.
   691  
   692  		if strings.HasSuffix(base64str, "=") {
   693  			base64str = base64str[:len(base64str)-1]
   694  			return Base64ToBytes(base64str)
   695  		}
   696  		return nil, errors.Wrapf(err, "failed to parse base64 string %s", base64str)
   697  	}
   698  
   699  	return data, nil
   700  }
   701  
   702  func BytesToBase64(b []byte) string {
   703  	data := base64.StdEncoding.EncodeToString(b)
   704  
   705  	return data
   706  }
   707  
   708  func GetCertificateFromPEMBytes(bytes []byte) (*x509.Certificate, error) {
   709  	block, _ := pem.Decode(bytes)
   710  	if block == nil {
   711  		return nil, errors.New("failed to decode PEM bytes")
   712  	}
   713  
   714  	cert, err := x509.ParseCertificate(block.Bytes)
   715  	if err != nil {
   716  		return nil, errors.Wrap(err, "failed to parse certificate")
   717  	}
   718  
   719  	return cert, nil
   720  }
   721  
   722  func WriteFile(file string, buf []byte, perm os.FileMode) error {
   723  	dir := path.Dir(file)
   724  	// Create the directory if it doesn't exist
   725  	if _, err := os.Stat(dir); os.IsNotExist(err) {
   726  		err = os.MkdirAll(dir, 0750)
   727  		if err != nil {
   728  			return errors.Wrapf(err, "Failed to create directory '%s' for file '%s'", dir, file)
   729  		}
   730  	}
   731  	return ioutil.WriteFile(file, buf, perm)
   732  }
   733  
   734  func CheckIfZoneOrRegionUpdated(oldValue string, newValue string) bool {
   735  	if (strings.ToLower(oldValue) != "select" && oldValue != "") && (strings.ToLower(newValue) != "select" && newValue != "") {
   736  		if oldValue != newValue {
   737  			return true
   738  		}
   739  	}
   740  
   741  	return false
   742  }
   743  
   744  func GenerateRandomString(length int) string {
   745  	const charset = "abcdefghijklmnopqrstuvwxyz" +
   746  		"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
   747  
   748  	b := make([]byte, length)
   749  	for i := range b {
   750  		num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
   751  		b[i] = charset[num.Int64()]
   752  	}
   753  	return string(b)
   754  }
   755  
   756  func ValidateHSMProxyURL(endpoint string) error {
   757  	parsedURL, err := url.Parse(endpoint)
   758  	if err != nil {
   759  		return err
   760  	}
   761  
   762  	address := strings.Split(parsedURL.Host, ":")
   763  	if len(address) < 2 {
   764  		return errors.New("must specify both IP address and port")
   765  	}
   766  
   767  	if address[0] == "" {
   768  		return errors.New("missing IP address")
   769  	}
   770  
   771  	if address[1] == "" {
   772  		return errors.New("missing port")
   773  	}
   774  
   775  	scheme := parsedURL.Scheme
   776  	if scheme != "tls" && scheme != "tcp" {
   777  		return fmt.Errorf("unsupported scheme '%s', only tcp and tls are supported", scheme)
   778  	}
   779  
   780  	if !IsTCPReachable(parsedURL.Host) {
   781  		return fmt.Errorf("Unable to reach HSM endpoint: %s", parsedURL.Host)
   782  	}
   783  	return nil
   784  }
   785  
   786  // func HealthCheck(caURL *url.URL, cert []byte) error {
   787  func HealthCheck(healthURL string, cert []byte, timeout time.Duration) error {
   788  	rootCertPool := x509.NewCertPool()
   789  	rootCertPool.AppendCertsFromPEM(cert)
   790  
   791  	transport := http.DefaultTransport
   792  	transport.(*http.Transport).TLSClientConfig = &tls.Config{
   793  		RootCAs:    rootCertPool,
   794  		MinVersion: tls.VersionTLS12, // TLS 1.2 recommended, TLS 1.3 (current latest version) encouraged
   795  	}
   796  
   797  	client := http.Client{
   798  		Transport: &http.Transport{
   799  			IdleConnTimeout: timeout,
   800  			Dial: (&net.Dialer{
   801  				Timeout:   timeout,
   802  				KeepAlive: timeout,
   803  			}).Dial,
   804  			TLSHandshakeTimeout: timeout / 2,
   805  			TLSClientConfig: &tls.Config{
   806  				RootCAs:    rootCertPool,
   807  				MinVersion: tls.VersionTLS12, // TLS 1.2 recommended, TLS 1.3 (current latest version) encouraged
   808  			},
   809  		},
   810  	}
   811  
   812  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   813  	defer cancel()
   814  
   815  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, healthURL, nil)
   816  	if err != nil {
   817  		return errors.Wrap(err, "invalid http request")
   818  	}
   819  
   820  	resp, err := client.Do(req)
   821  	if err != nil {
   822  		return errors.Wrapf(err, "health check request failed")
   823  	}
   824  
   825  	if resp.StatusCode != http.StatusOK {
   826  		return errors.Wrapf(err, "failed health check, ca is not running")
   827  	}
   828  
   829  	return nil
   830  }
   831  
   832  func IsTCPReachable(url string) bool {
   833  	url = strings.Replace(url, "tcp://", "", -1)
   834  	url = strings.Replace(url, "tls://", "", -1)
   835  
   836  	conn, err := net.Dial("tcp", url)
   837  	if err != nil {
   838  		return false
   839  	}
   840  
   841  	defer conn.Close()
   842  
   843  	return true
   844  }
   845  
   846  func IntermediateSecretExists(client Client, namespace, secretName string) bool {
   847  	err := client.Get(context.TODO(), types.NamespacedName{
   848  		Name:      secretName,
   849  		Namespace: namespace}, &corev1.Secret{})
   850  	if err != nil {
   851  		return false
   852  	}
   853  
   854  	return true
   855  }
   856  
   857  func IsSecretTLSCert(secretName string) bool {
   858  	if strings.HasSuffix(secretName, "-signcert") {
   859  		return strings.HasPrefix(secretName, "tls")
   860  	} else if strings.HasSuffix(secretName, "-ca-crypto") {
   861  		return true
   862  	}
   863  
   864  	return false
   865  }
   866  
   867  func IsSecretEcert(secretName string) bool {
   868  	if strings.HasSuffix(secretName, "-signcert") {
   869  		return strings.HasPrefix(secretName, "ecert")
   870  	}
   871  
   872  	return false
   873  }
   874  
   875  func ConvertSpec(in interface{}, out interface{}) error {
   876  	jsonBytes, err := yaml1.Marshal(in)
   877  	if err != nil {
   878  		return err
   879  	}
   880  
   881  	err = yaml1.Unmarshal(jsonBytes, out)
   882  	if err != nil {
   883  		return err
   884  	}
   885  	return nil
   886  }
   887  
   888  func FindStringInArray(str string, slice []string) bool {
   889  	for _, item := range slice {
   890  		if item == str {
   891  			return true
   892  		}
   893  	}
   894  	return false
   895  }
   896  
   897  func ConvertToJsonMessage(in interface{}) (*json.RawMessage, error) {
   898  	bytes, err := json.Marshal(in)
   899  	if err != nil {
   900  		return nil, err
   901  	}
   902  
   903  	jm := json.RawMessage(bytes)
   904  	return &jm,
   905  
   906  		nil
   907  }
   908  
   909  func GetNetworkPolicyFromFile(file string) (*networkingv1.NetworkPolicy, error) {
   910  	jsonBytes, err := ConvertYamlFileToJson(file)
   911  	if err != nil {
   912  		return nil, err
   913  	}
   914  
   915  	policy := &networkingv1.NetworkPolicy{}
   916  	err = json.Unmarshal(jsonBytes, &policy)
   917  	if err != nil {
   918  		return nil, err
   919  	}
   920  
   921  	return policy, nil
   922  }
   923  
   924  func GetServerVersion() (*version.Info, error) {
   925  	config, err := rest.InClusterConfig()
   926  	if err != nil {
   927  		return nil, errors.Wrap(err, "failed to get cluster config")
   928  	}
   929  
   930  	clientSet, err := clientset.New(config)
   931  	if err != nil {
   932  		return nil, errors.Wrap(err, "failed to get client")
   933  	}
   934  
   935  	version, err := clientSet.DiscoveryClient.ServerVersion()
   936  	if err != nil {
   937  		return nil, errors.Wrap(err, "failed to get version")
   938  	}
   939  	return version, nil
   940  }