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 }