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

     1  package create
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/olli-ai/jx/v2/pkg/cmd/create/options"
     8  
     9  	"github.com/olli-ai/jx/v2/pkg/cmd/helper"
    10  
    11  	"github.com/olli-ai/jx/v2/pkg/helm"
    12  
    13  	"github.com/jenkins-x/jx-logging/pkg/log"
    14  	networkingv1alpha3 "istio.io/api/networking/v1alpha3"
    15  	istiov1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
    16  
    17  	"github.com/olli-ai/jx/v2/pkg/kube"
    18  	"github.com/pkg/errors"
    19  
    20  	"github.com/olli-ai/jx/v2/pkg/cmd/opts"
    21  	"github.com/olli-ai/jx/v2/pkg/cmd/templates"
    22  	"github.com/olli-ai/jx/v2/pkg/util"
    23  	"github.com/spf13/cobra"
    24  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/types"
    27  )
    28  
    29  const (
    30  	DefaultFlaggerNamespace             = DefaultIstioNamespace
    31  	defaultFlaggerReleaseName           = kube.DefaultFlaggerReleaseName
    32  	defaultFlaggerVersion               = ""
    33  	defaultFlaggerRepo                  = "https://flagger.app"
    34  	optionGrafanaChart                  = "grafana-chart"
    35  	optionGrafanaVersion                = "grafana-version"
    36  	defaultFlaggerProductionEnvironment = "production"
    37  	defaultIstioGateway                 = "jx-gateway"
    38  )
    39  
    40  var (
    41  	createAddonFlaggerLong = templates.LongDesc(`
    42  		Creates the Flagger addon
    43  `)
    44  
    45  	createAddonFlaggerExample = templates.Examples(`
    46  		# Create the Flagger addon
    47  		jx create addon flagger
    48  	`)
    49  )
    50  
    51  type CreateAddonFlaggerOptions struct {
    52  	CreateAddonOptions
    53  	Chart                 string
    54  	GrafanaChart          string
    55  	GrafanaVersion        string
    56  	ProductionEnvironment string
    57  	IstioGateway          string
    58  }
    59  
    60  func NewCmdCreateAddonFlagger(commonOpts *opts.CommonOptions) *cobra.Command {
    61  	options := &CreateAddonFlaggerOptions{
    62  		CreateAddonOptions: CreateAddonOptions{
    63  			CreateOptions: options.CreateOptions{
    64  				CommonOptions: commonOpts,
    65  			},
    66  		},
    67  	}
    68  
    69  	cmd := &cobra.Command{
    70  		Use:     "flagger",
    71  		Short:   "Create the Flagger addon for Canary deployments",
    72  		Long:    createAddonFlaggerLong,
    73  		Example: createAddonFlaggerExample,
    74  		Run: func(cmd *cobra.Command, args []string) {
    75  			err := options.Run()
    76  			helper.CheckErr(err)
    77  		},
    78  	}
    79  
    80  	options.addFlags(cmd, DefaultFlaggerNamespace, defaultFlaggerReleaseName, defaultFlaggerVersion)
    81  
    82  	cmd.Flags().StringVarP(&options.Chart, optionChart, "c", kube.ChartFlagger, "The name of the chart to use")
    83  	cmd.Flags().StringVarP(&options.GrafanaChart, optionGrafanaChart, "", kube.ChartFlaggerGrafana, "The name of the Flagger Grafana chart to use")
    84  	cmd.Flags().StringVarP(&options.GrafanaVersion, optionGrafanaVersion, "", "", "The version of the Flagger Grafana chart")
    85  	cmd.Flags().StringVarP(&options.ProductionEnvironment, "environment", "e", defaultFlaggerProductionEnvironment, "The name of the production environment where Istio will be enabled")
    86  	cmd.Flags().StringVarP(&options.IstioGateway, "istio-gateway", "", defaultIstioGateway, "The name of the Istio Gateway that will be created if it does not exist")
    87  	return cmd
    88  }
    89  
    90  // Create the addon
    91  func (o *CreateAddonFlaggerOptions) Run() error {
    92  	if o.ReleaseName == "" {
    93  		return util.MissingOption(optionRelease)
    94  	}
    95  	if o.Chart == "" {
    96  		return util.MissingOption(optionChart)
    97  	}
    98  	if o.GrafanaChart == "" {
    99  		return util.MissingOption(optionGrafanaChart)
   100  	}
   101  	err := o.EnsureHelm()
   102  	if err != nil {
   103  		return errors.Wrap(err, "failed to ensure that Helm is present")
   104  	}
   105  
   106  	values := []string{}
   107  	setValues := strings.Split(o.SetValues, ",")
   108  	values = append(values, setValues...)
   109  	_, err = o.AddHelmBinaryRepoIfMissing(defaultFlaggerRepo, "flagger", "", "")
   110  	if err != nil {
   111  		return errors.Wrap(err, "Flagger deployment failed")
   112  	}
   113  	helmOptions := helm.InstallChartOptions{
   114  		Chart:       o.Chart,
   115  		ReleaseName: o.ReleaseName,
   116  		Version:     o.Version,
   117  		Ns:          o.Namespace,
   118  		SetValues:   values,
   119  	}
   120  	err = o.InstallChartWithOptions(helmOptions)
   121  	if err != nil {
   122  		return errors.Wrap(err, "Flagger deployment failed")
   123  	}
   124  	helmOptions = helm.InstallChartOptions{
   125  		Chart:       o.GrafanaChart,
   126  		ReleaseName: o.ReleaseName + "-grafana",
   127  		Version:     o.GrafanaVersion,
   128  		Ns:          o.Namespace,
   129  		SetValues:   values,
   130  	}
   131  	err = o.InstallChartWithOptions(helmOptions)
   132  	if err != nil {
   133  		return errors.Wrap(err, "Flagger Grafana deployment failed")
   134  	}
   135  
   136  	// Enable Istio in production namespace
   137  	if o.ProductionEnvironment != "" {
   138  		client, err := o.KubeClient()
   139  		if err != nil {
   140  			return errors.Wrap(err, "error enabling Istio in production namespace")
   141  		}
   142  		var ns string
   143  		ns, err = o.FindEnvironmentNamespace(o.ProductionEnvironment)
   144  		if err != nil {
   145  			return errors.Wrap(err, fmt.Sprintf("error enabling Istio for environment %s", o.ProductionEnvironment))
   146  		}
   147  		log.Logger().Infof("Enabling Istio in namespace %s", ns)
   148  		patch := []byte(`{"metadata":{"labels":{"istio-injection":"enabled"}}}`)
   149  		_, err = client.CoreV1().Namespaces().Patch(ns, types.MergePatchType, patch)
   150  		if err != nil {
   151  			return errors.Wrap(err, fmt.Sprintf("error enabling Istio in namespace %s", ns))
   152  		}
   153  	}
   154  
   155  	// Create the Istio gateway
   156  	if o.IstioGateway != "" {
   157  		istioClient, err := o.IstioClient()
   158  		if err != nil {
   159  			return errors.Wrap(err, "error building Istio client")
   160  		}
   161  		gateway, err := istioClient.NetworkingV1alpha3().Gateways(DefaultIstioNamespace).Get(o.IstioGateway, metav1.GetOptions{})
   162  		if apierrors.IsNotFound(err) {
   163  			gateway = &istiov1alpha3.Gateway{
   164  				ObjectMeta: metav1.ObjectMeta{
   165  					Name:      o.IstioGateway,
   166  					Namespace: DefaultIstioNamespace,
   167  				},
   168  				Spec: networkingv1alpha3.Gateway{
   169  					Selector: map[string]string{"istio": "ingressgateway"},
   170  					Servers: []*networkingv1alpha3.Server{
   171  						// TODO add https port if enabled
   172  						{
   173  							Port: &networkingv1alpha3.Port{
   174  								Number:   80,
   175  								Name:     "http",
   176  								Protocol: "HTTP",
   177  							},
   178  							Hosts: []string{"*"},
   179  						},
   180  					},
   181  				},
   182  			}
   183  
   184  			log.Logger().Infof("Creating Istio gateway: %s", o.IstioGateway)
   185  			gateway, err = istioClient.NetworkingV1alpha3().Gateways(DefaultIstioNamespace).Create(gateway)
   186  			if err != nil {
   187  				return errors.Wrap(err, "error creating Istio gateway")
   188  			}
   189  		} else {
   190  			log.Logger().Infof("Istio gateway already exists: %s", o.IstioGateway)
   191  		}
   192  	}
   193  	return nil
   194  }