github.com/devcamcar/cli@v0.0.0-20181107134215-706a05759d18/objects/context/context.go (about)

     1  package context
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net/url"
     9  	"os"
    10  	"path/filepath"
    11  	"regexp"
    12  	"strings"
    13  	"text/tabwriter"
    14  
    15  	"github.com/fnproject/cli/config"
    16  	"github.com/fnproject/fn_go/provider"
    17  	"github.com/spf13/viper"
    18  	"github.com/urfave/cli"
    19  )
    20  
    21  var contextsPath = config.GetContextsPath()
    22  var fileExtension = ".yaml"
    23  
    24  type ContextMap config.ContextMap
    25  
    26  func createCtx(c *cli.Context) error {
    27  	context := c.Args().Get(0)
    28  
    29  	err := ValidateContextName(context)
    30  	if err != nil {
    31  		return err
    32  	}
    33  
    34  	providerId := config.DefaultProvider
    35  	if cProvider := c.String("provider"); cProvider != "" {
    36  		providerId = cProvider
    37  	}
    38  
    39  	apiURL := ""
    40  	if cApiURL := c.String("api-url"); cApiURL != "" {
    41  		err = ValidateAPIURL(cApiURL)
    42  		if err != nil {
    43  			return err
    44  		}
    45  		apiURL = cApiURL
    46  	}
    47  
    48  	registry := ""
    49  	if cRegistry := c.String("registry"); cRegistry != "" {
    50  		registry = cRegistry
    51  	}
    52  
    53  	if check, err := checkContextFileExists(context); check {
    54  		if err != nil {
    55  			return err
    56  		}
    57  		return errors.New("Context already exists")
    58  	}
    59  	path := createFilePath(context + fileExtension)
    60  	file, err := os.Create(path)
    61  	if err != nil {
    62  		return err
    63  	}
    64  	defer file.Close()
    65  
    66  	contextValues := &config.ContextMap{
    67  		config.ContextProvider: providerId,
    68  		provider.CfgFnAPIURL:   apiURL,
    69  		config.EnvFnRegistry:   registry,
    70  	}
    71  
    72  	err = config.WriteYamlFile(file.Name(), contextValues)
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	fmt.Printf("Successfully created context: %v \n", context)
    78  	return nil
    79  }
    80  
    81  func deleteCtx(c *cli.Context) error {
    82  	context := c.Args().Get(0)
    83  
    84  	if check, err := checkContextFileExists(context); !check {
    85  		if err != nil {
    86  			return err
    87  		}
    88  		return errors.New("Context file not found")
    89  	}
    90  
    91  	if context == viper.GetString(config.CurrentContext) {
    92  		return fmt.Errorf("Cannot delete the current context: %v", context)
    93  	}
    94  
    95  	if context == "default" {
    96  		return errors.New("Cannot delete default context")
    97  	}
    98  
    99  	path := createFilePath(context + fileExtension)
   100  	err := os.Remove(path)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	fmt.Printf("Successfully deleted context %v \n", context)
   106  	return nil
   107  }
   108  
   109  func inspectCtx(c *cli.Context) error {
   110  	context := c.Args().Get(0)
   111  	if context == "" {
   112  		if currentContext := viper.GetString(config.CurrentContext); currentContext != "" {
   113  			context = currentContext
   114  		} else {
   115  			return errors.New("no context is set, please provider a context to inspect.")
   116  		}
   117  	}
   118  	return printContext(context)
   119  }
   120  
   121  func printContext(context string) error {
   122  	if check, err := checkContextFileExists(context); !check {
   123  		if err != nil {
   124  			return err
   125  		}
   126  		return errors.New("Context file not found")
   127  	}
   128  
   129  	contextPath := filepath.Join(config.GetHomeDir(), ".fn", "contexts", (context + fileExtension))
   130  	b, err := ioutil.ReadFile(contextPath)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	fmt.Printf("Current context: %s\n\n", context)
   136  	fmt.Println(string(b))
   137  	return nil
   138  }
   139  
   140  func useCtx(c *cli.Context) error {
   141  	context := c.Args().Get(0)
   142  
   143  	if check, err := checkContextFileExists(context); !check {
   144  		if err != nil {
   145  			return err
   146  		}
   147  		return errors.New("Context file not found")
   148  	}
   149  
   150  	if context == viper.GetString(config.CurrentContext) {
   151  		return fmt.Errorf("Context %v currently in use", context)
   152  	}
   153  
   154  	err := config.WriteConfigValueToConfigFile(config.CurrentContext, context)
   155  	if err != nil {
   156  		return err
   157  	}
   158  	viper.Set(config.CurrentContext, context)
   159  
   160  	fmt.Printf("Now using context: %v \n", context)
   161  	return nil
   162  }
   163  
   164  func unsetCtx(c *cli.Context) error {
   165  	if currentContext := viper.GetString(config.CurrentContext); currentContext == "" {
   166  		return errors.New("No context currently in use")
   167  	}
   168  
   169  	err := config.WriteConfigValueToConfigFile(config.CurrentContext, "")
   170  	if err != nil {
   171  		return err
   172  	}
   173  
   174  	fmt.Printf("Successfully unset current context \n")
   175  	return nil
   176  }
   177  
   178  func printContexts(c *cli.Context, contexts []*Info) error {
   179  	outputFormat := strings.ToLower(c.String("output"))
   180  	if outputFormat == "json" {
   181  		b, err := json.MarshalIndent(contexts, "", "    ")
   182  		if err != nil {
   183  			return err
   184  		}
   185  		fmt.Fprint(os.Stdout, string(b))
   186  	} else {
   187  		w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
   188  		fmt.Fprint(w, "CURRENT", "\t", "NAME", "\t", "PROVIDER", "\t", "API URL", "\t", "REGISTRY", "\n")
   189  
   190  		for _, ctx := range contexts {
   191  			current := ""
   192  			if ctx.Current {
   193  				current = "*"
   194  			}
   195  			fmt.Fprint(w, current, "\t", ctx.Name, "\t", ctx.ContextProvider, "\t", ctx.EnvFnAPIURL, "\t", ctx.EnvFnRegistry, "\n")
   196  		}
   197  		if err := w.Flush(); err != nil {
   198  			return err
   199  		}
   200  	}
   201  	return nil
   202  }
   203  
   204  func listCtx(c *cli.Context) error {
   205  	contexts, err := getAvailableContexts()
   206  	if err != nil {
   207  		return err
   208  	}
   209  	return printContexts(c, contexts)
   210  }
   211  
   212  func (ctxMap *ContextMap) updateCtx(c *cli.Context) error {
   213  	key := c.Args().Get(0)
   214  
   215  	delete := c.Bool("delete")
   216  	if delete {
   217  		err := ctxMap.UnSet(key)
   218  		if err != nil {
   219  			return err
   220  		}
   221  		fmt.Printf("Current context deleted %v \n", key)
   222  		return nil
   223  	}
   224  
   225  	value := c.Args().Get(1)
   226  	if value == "" {
   227  		return errors.New("Please specify a value")
   228  	}
   229  
   230  	err := ctxMap.Set(key, value)
   231  	if err != nil {
   232  		return err
   233  	}
   234  
   235  	fmt.Printf("Current context updated %v with %v\n", key, value)
   236  	return err
   237  }
   238  
   239  func createFilePath(filename string) string {
   240  	home := config.GetHomeDir()
   241  	return filepath.Join(home, contextsPath, filename)
   242  }
   243  
   244  func checkContextFileExists(filename string) (bool, error) {
   245  	path := createFilePath(filename + fileExtension)
   246  
   247  	if _, err := os.Stat(path); os.IsNotExist(err) {
   248  		return false, err
   249  	}
   250  	return true, nil
   251  }
   252  
   253  func getAvailableContexts() ([]*Info, error) {
   254  	home := config.GetHomeDir()
   255  	files, err := ioutil.ReadDir(filepath.Join(home, contextsPath))
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	currentContext := viper.GetString(config.CurrentContext)
   261  	var contexts []*Info
   262  	for _, f := range files {
   263  		fullPath := getContextFilePath(f.Name())
   264  		ctxFile, err := config.NewContextFile(fullPath)
   265  		if err != nil {
   266  			return nil, err
   267  		}
   268  
   269  		isCurrent := false
   270  		name := strings.Replace(f.Name(), fileExtension, "", 1)
   271  		if currentContext == name {
   272  			isCurrent = true
   273  		}
   274  		c := NewInfo(name, isCurrent, ctxFile)
   275  		contexts = append(contexts, c)
   276  	}
   277  	return contexts, err
   278  }
   279  
   280  // getContextFilePath returns the full path to
   281  // the context file
   282  func getContextFilePath(name string) string {
   283  	home := config.GetHomeDir()
   284  	return filepath.Join(home, contextsPath, name)
   285  }
   286  
   287  func ValidateAPIURL(apiURL string) error {
   288  	if !strings.Contains(apiURL, "://") {
   289  		return errors.New("Invalid Fn API URL: does not contain ://")
   290  	}
   291  
   292  	_, err := url.Parse(apiURL)
   293  	if err != nil {
   294  		return fmt.Errorf("Invalid Fn API URL: %s", err)
   295  	}
   296  	return nil
   297  }
   298  
   299  func ValidateContextName(context string) error {
   300  	re := regexp.MustCompile("[^a-zA-Z0-9_-]+")
   301  
   302  	for range re.FindAllString(context, -1) {
   303  		return errors.New("Please enter a context name with only Alphanumeric, _, or -")
   304  	}
   305  	return nil
   306  }
   307  
   308  func (ctxMap *ContextMap) Set(key, value string) error {
   309  	contextFilePath := createFilePath(viper.GetString(config.CurrentContext) + fileExtension)
   310  	f, err := os.OpenFile(contextFilePath, os.O_RDWR, config.ReadWritePerms)
   311  	if err != nil {
   312  		return err
   313  	}
   314  	defer f.Close()
   315  
   316  	file, err := config.DecodeYAMLFile(f.Name())
   317  	if err != nil {
   318  		return err
   319  	}
   320  
   321  	if key == provider.CfgFnAPIURL {
   322  		err := ValidateAPIURL(value)
   323  		if err != nil {
   324  			return err
   325  		}
   326  	}
   327  
   328  	(*file)[key] = value
   329  	return config.WriteYamlFile(f.Name(), file)
   330  }
   331  
   332  func (ctxMap *ContextMap) UnSet(key string) error {
   333  	contextFilePath := createFilePath(viper.GetString(config.CurrentContext) + fileExtension)
   334  	f, err := os.OpenFile(contextFilePath, os.O_RDWR, config.ReadWritePerms)
   335  	if err != nil {
   336  		return err
   337  	}
   338  	defer f.Close()
   339  
   340  	file, err := config.DecodeYAMLFile(f.Name())
   341  	if err != nil {
   342  		return err
   343  	}
   344  
   345  	if _, ok := (*file)[key]; !ok {
   346  		return errors.New("Context file does not contain key: " + key)
   347  	}
   348  
   349  	delete((*file), key)
   350  	return config.WriteYamlFile(f.Name(), file)
   351  }