github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cmd/step/step_link_services.go (about)

     1  package step
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/olli-ai/jx/v2/pkg/cmd/opts/step"
     7  
     8  	"github.com/olli-ai/jx/v2/pkg/cmd/helper"
     9  
    10  	"github.com/jenkins-x/jx-logging/pkg/log"
    11  	"github.com/olli-ai/jx/v2/pkg/cmd/opts"
    12  	"github.com/olli-ai/jx/v2/pkg/cmd/templates"
    13  	"github.com/olli-ai/jx/v2/pkg/util"
    14  	"github.com/spf13/cobra"
    15  	corev1 "k8s.io/api/core/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  )
    18  
    19  const (
    20  	fromNamespace = "from-namespace"
    21  	toNamespace   = "to-namespace"
    22  	includes      = "includes"
    23  	excludes      = "excludes"
    24  )
    25  
    26  var (
    27  	StepLinkServicesLong = templates.LongDesc(`
    28  		This pipeline step helps to link microservices from different namespaces like staging/production onto a preview environment
    29  `)
    30  
    31  	StepLinkServicesExample = templates.Examples(`
    32  	#Link services from jx-staging namespace to the current namespace
    33  	jx step link services --from-namespace jx-staging 
    34  
    35  	#Link services from jx-staging namespace to the jx-prod namespace
    36  	jx step link services --from-namespace jx-staging --to-namespace jx-prod
    37  	
    38  	#Link services from jx-staging namespace to the jx-prod namespace including all but the ones starting with  the characters 'cheese'
    39  	jx step link services --from-namespace jx-staging --to-namespace jx-prod --includes * --excludes cheese*
    40  `)
    41  )
    42  
    43  // StepLinkServicesOptions contains the command line flags
    44  type StepLinkServicesOptions struct {
    45  	step.StepOptions
    46  	FromNamespace string
    47  	ToNamespace   string
    48  	Includes      []string
    49  	Excludes      []string
    50  }
    51  
    52  // NewCmdStepLinkServices Creates a new Command object
    53  func NewCmdStepLinkServices(commonOpts *opts.CommonOptions) *cobra.Command {
    54  	options := &StepLinkServicesOptions{
    55  		StepOptions: step.StepOptions{
    56  			CommonOptions: commonOpts,
    57  		},
    58  	}
    59  
    60  	cmd := &cobra.Command{
    61  		Use:     "link services",
    62  		Short:   "achieve service linking in preview environments",
    63  		Long:    StepLinkServicesLong,
    64  		Example: StepLinkServicesExample,
    65  		Run: func(cmd *cobra.Command, args []string) {
    66  			options.Cmd = cmd
    67  			options.Args = args
    68  			err := options.Run()
    69  			helper.CheckErr(err)
    70  		},
    71  	}
    72  
    73  	cmd.Flags().StringVarP(&options.FromNamespace, fromNamespace, "f", "", "The source namespace from which the linking would happen")
    74  	cmd.Flags().StringVarP(&options.ToNamespace, toNamespace, "t", "", "The destination namespace to which the linking would happen")
    75  	cmd.Flags().StringArrayVarP(&options.Includes, includes, "i", []string{}, "What services from source namespace to include in the linking process")
    76  	cmd.Flags().StringArrayVarP(&options.Excludes, excludes, "e", []string{}, "What services from the source namespace to exclude from the linking process")
    77  	return cmd
    78  }
    79  
    80  // Run implements this command
    81  func (o *StepLinkServicesOptions) Run() error {
    82  	fromNs := o.FromNamespace
    83  	if fromNs == "" {
    84  		return util.MissingOption(fromNamespace)
    85  	}
    86  	kubeClient, currentNs, err := o.KubeClientAndNamespace()
    87  	if err != nil {
    88  		return err
    89  	}
    90  	targetNamespace := o.ToNamespace
    91  	if targetNamespace == "" {
    92  		//to-namespace wasn't supplied, let's assume it is current namespace
    93  		targetNamespace = currentNs
    94  	}
    95  	if targetNamespace == "" {
    96  		//We don't want to continue if we still can't derive to-namespace
    97  		return util.MissingOption(toNamespace)
    98  	} else {
    99  		serviceList, err := kubeClient.CoreV1().Services(fromNs).List(metav1.ListOptions{})
   100  		if err != nil {
   101  			return err
   102  		} else {
   103  			for _, service := range serviceList.Items {
   104  				name := service.GetName()
   105  				if util.StringMatchesAny(name, o.Includes, o.Excludes) {
   106  					targetService, err := kubeClient.CoreV1().Services(targetNamespace).Get(name, metav1.GetOptions{})
   107  					create := false
   108  					if err != nil {
   109  						copy := corev1.Service{}
   110  						targetService = &copy
   111  						create = true
   112  					}
   113  					targetService.Name = name
   114  					targetService.Namespace = targetNamespace
   115  					targetService.Annotations = service.Annotations
   116  					targetService.Labels = service.Labels
   117  					targetService.Spec = corev1.ServiceSpec{
   118  						Type:         corev1.ServiceTypeExternalName,
   119  						ExternalName: fmt.Sprintf("%s.%s.svc.cluster.local", name, fromNs),
   120  					}
   121  
   122  					if create {
   123  						_, err := kubeClient.CoreV1().Services(targetNamespace).Create(targetService)
   124  						if err != nil {
   125  							log.Logger().Warnf("Failed to create the service '%s' in target namespace '%s'. Error: %s",
   126  								name, targetNamespace, err)
   127  						}
   128  					} else {
   129  						_, err := kubeClient.CoreV1().Services(targetNamespace).Update(targetService)
   130  						if err != nil {
   131  							log.Logger().Warnf("Failed to update the service '%s' in target namespace '%s'. Error: %s",
   132  								name, targetNamespace, err)
   133  						}
   134  					}
   135  				}
   136  			}
   137  		}
   138  	}
   139  	return nil
   140  }