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

     1  package create
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/olli-ai/jx/v2/pkg/cmd/create/options"
     8  
     9  	"github.com/olli-ai/jx/v2/pkg/cmd/edit"
    10  	"github.com/olli-ai/jx/v2/pkg/packages"
    11  
    12  	"github.com/olli-ai/jx/v2/pkg/cmd/helper"
    13  
    14  	"github.com/jenkins-x/jx-logging/pkg/log"
    15  	"github.com/olli-ai/jx/v2/pkg/util"
    16  	"github.com/pkg/errors"
    17  	"github.com/spf13/cobra"
    18  	"k8s.io/client-go/kubernetes"
    19  
    20  	"github.com/olli-ai/jx/v2/pkg/cmd/opts"
    21  	"github.com/olli-ai/jx/v2/pkg/cmd/templates"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  )
    24  
    25  const (
    26  	glooNamespace           = "gloo-system"
    27  	glooClusterIngressProxy = "clusteringress-proxy"
    28  	knativeServeNamespace   = "knative-serving"
    29  	knativeServeConfig      = "config-domain"
    30  )
    31  
    32  var (
    33  	createAddonGlooLong = templates.LongDesc(`
    34  		Create a Gloo and Knative Serve addon for creating serverless applications
    35  `)
    36  
    37  	createAddonGlooExample = templates.Examples(`
    38  		# Create the Gloo addon 
    39  		jx create addon gloo
    40  	`)
    41  )
    42  
    43  // CreateAddonGlooOptions the options for the create spring command
    44  type CreateAddonGlooOptions struct {
    45  	CreateAddonOptions
    46  
    47  	GlooNamespace         string
    48  	ClusterIngressProxy   string
    49  	KnativeServeNamespace string
    50  	KnativeServeConfigMap string
    51  }
    52  
    53  // NewCmdCreateAddonGloo creates a command object for the "create" command
    54  func NewCmdCreateAddonGloo(commonOpts *opts.CommonOptions) *cobra.Command {
    55  	options := &CreateAddonGlooOptions{
    56  		CreateAddonOptions: CreateAddonOptions{
    57  			CreateOptions: options.CreateOptions{
    58  				CommonOptions: commonOpts,
    59  			},
    60  		},
    61  	}
    62  
    63  	cmd := &cobra.Command{
    64  		Use:     "gloo",
    65  		Short:   "Create a Gloo and Knative Serve addon for creating serverless applications",
    66  		Aliases: []string{"knative-serve"},
    67  		Long:    createAddonGlooLong,
    68  		Example: createAddonGlooExample,
    69  		Run: func(cmd *cobra.Command, args []string) {
    70  			options.Cmd = cmd
    71  			options.Args = args
    72  			err := options.Run()
    73  			helper.CheckErr(err)
    74  		},
    75  	}
    76  	cmd.Flags().StringVarP(&options.GlooNamespace, "namespace", "n", glooNamespace, "The gloo system namespace")
    77  	cmd.Flags().StringVarP(&options.ClusterIngressProxy, "ingress", "i", glooClusterIngressProxy, "The name of the gloo cluster ingress proxy Service")
    78  	cmd.Flags().StringVarP(&options.KnativeServeNamespace, "knative-namespace", "k", knativeServeNamespace, "The knative serving namespace")
    79  	cmd.Flags().StringVarP(&options.KnativeServeConfigMap, "knative-configmap", "c", knativeServeConfig, "The knative serving ConfigMap name")
    80  
    81  	return cmd
    82  }
    83  
    84  // Run implements the command
    85  func (o *CreateAddonGlooOptions) Run() error {
    86  	if o.GlooNamespace == "" {
    87  		o.GlooNamespace = glooNamespace
    88  	}
    89  	if o.ClusterIngressProxy == "" {
    90  		o.ClusterIngressProxy = glooClusterIngressProxy
    91  	}
    92  	if o.KnativeServeNamespace == "" {
    93  		o.KnativeServeNamespace = knativeServeNamespace
    94  	}
    95  	if o.KnativeServeConfigMap == "" {
    96  		o.KnativeServeConfigMap = knativeServeConfig
    97  	}
    98  
    99  	// lets ensure glooctl is installed
   100  	shouldInstall, err := packages.ShouldInstallBinary("glooctl")
   101  	if err != nil {
   102  		return errors.Wrapf(err, "failed to check if we need to install glooctl")
   103  	}
   104  
   105  	if shouldInstall {
   106  		log.Logger().Infof("installing %s", util.ColorInfo("glooctl"))
   107  		err = o.InstallGlooctl()
   108  		if err != nil {
   109  			return errors.Wrapf(err, "failed to install glooctl")
   110  		}
   111  	}
   112  
   113  	kubeClient, err := o.KubeClient()
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	// lets try load the gloo cluster service
   119  	_, err = kubeClient.CoreV1().Services(o.GlooNamespace).Get(o.ClusterIngressProxy, metav1.GetOptions{})
   120  	if err != nil {
   121  		// we may not have installed gloo yet so lets do that now...
   122  		err = o.RunCommandVerbose("glooctl", "install", "knative")
   123  		if err != nil {
   124  			return errors.Wrapf(err, "failed to install gloo")
   125  		}
   126  	}
   127  
   128  	ip, err := o.getGlooDomain(kubeClient)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	if ip == "" {
   133  		return fmt.Errorf("failed to find the external LoadBalancer IP of the Gloo cluster ingress proxy service %s in namespace %s", o.ClusterIngressProxy, o.GlooNamespace)
   134  	}
   135  	externalDomain := ip + ".nip.io"
   136  	err = o.updateKnativeServeDomain(kubeClient, externalDomain)
   137  	if err != nil {
   138  		return errors.Wrapf(err, "failed to update the gloo domain")
   139  	}
   140  
   141  	eo := &edit.EditDeployKindOptions{}
   142  	eo.CommonOptions = o.CommonOptions
   143  	eo.Team = true
   144  	eo.Kind = opts.DeployKindKnative
   145  	return eo.Run()
   146  }
   147  
   148  func (o *CreateAddonGlooOptions) getGlooDomain(kubeClient kubernetes.Interface) (string, error) {
   149  	loggedWait := false
   150  	ip := ""
   151  	fn := func() (bool, error) {
   152  		svc, err := kubeClient.CoreV1().Services(o.GlooNamespace).Get(o.ClusterIngressProxy, metav1.GetOptions{})
   153  		if err != nil {
   154  			return false, err
   155  		}
   156  
   157  		// lets get the gloo ingress service
   158  		for _, lb := range svc.Status.LoadBalancer.Ingress {
   159  			ip = lb.IP
   160  			if ip != "" {
   161  				return true, nil
   162  			}
   163  		}
   164  
   165  		if !loggedWait {
   166  			loggedWait = true
   167  			log.Logger().Infof("waiting for external IP on Gloo cluster ingress proxy service %s in namespace %s ...", o.ClusterIngressProxy, o.GlooNamespace)
   168  		}
   169  		return false, nil
   170  	}
   171  	err := o.RetryUntilTrueOrTimeout(time.Minute*5, time.Second*3, fn)
   172  	if ip == "" || err != nil {
   173  		return "", err
   174  	}
   175  	log.Logger().Infof("using external IP of gloo LoadBalancer: %s", util.ColorInfo(ip))
   176  	return ip, nil
   177  }
   178  
   179  func (o *CreateAddonGlooOptions) updateKnativeServeDomain(kubeClient kubernetes.Interface, domain string) error {
   180  	// lets get the knative serving ConfigMap
   181  	knativeConfigMaps := kubeClient.CoreV1().ConfigMaps(o.KnativeServeNamespace)
   182  	cm, err := knativeConfigMaps.Get(o.KnativeServeConfigMap, metav1.GetOptions{})
   183  	if err != nil {
   184  		return errors.Wrapf(err, "failed to load the Knative Serve ConfigMap %s in namespace %s", o.KnativeServeConfigMap, o.KnativeServeNamespace)
   185  	}
   186  	cm.Data = map[string]string{
   187  		domain: "",
   188  	}
   189  	_, err = knativeConfigMaps.Update(cm)
   190  	if err != nil {
   191  		return errors.Wrapf(err, "failed to save the Knative Serve ConfigMap %s in namespace %s", o.KnativeServeConfigMap, o.KnativeServeNamespace)
   192  	}
   193  	return nil
   194  }