github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/util/config/config.go (about) 1 // Package config contains helper methods for 2 // client side config management (`~/.micro/config.json` file). 3 // It uses the `JSONValues` helper 4 package config 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 "sync" 13 14 conf "github.com/tickoalcantara12/micro/v3/service/config" 15 "github.com/tickoalcantara12/micro/v3/util/user" 16 "github.com/nightlyone/lockfile" 17 ) 18 19 var ( 20 21 // lock in single process 22 mtx sync.Mutex 23 24 // File is the filepath to the config file that 25 // contains all user configuration 26 File = filepath.Join(user.Dir, "config.json") 27 28 // a global lock for the config 29 lock, _ = lockfile.New(File) 30 ) 31 32 // SetConfig sets the config file 33 func SetConfig(configFilePath string) { 34 mtx.Lock() 35 defer mtx.Unlock() 36 37 File = configFilePath 38 // new lock for the file 39 lock, _ = lockfile.New(File) 40 } 41 42 // config is a singleton which is required to ensure 43 // each function call doesn't load the .micro file 44 // from disk 45 46 // Get a value from the .micro file 47 func Get(path string) (string, error) { 48 mtx.Lock() 49 defer mtx.Unlock() 50 51 config, err := newConfig() 52 if err != nil { 53 return "", err 54 } 55 56 val := config.Get(path) 57 v := strings.TrimSpace(val.String("")) 58 if len(v) > 0 { 59 return v, nil 60 } 61 62 // try as bytes 63 v = string(val.Bytes()) 64 v = strings.TrimSpace(v) 65 66 // don't return nil decoded value 67 if strings.TrimSpace(v) == "null" { 68 return "", nil 69 } 70 71 return v, nil 72 } 73 74 // Set a value in the .micro file 75 func Set(path, value string) error { 76 mtx.Lock() 77 defer mtx.Unlock() 78 79 config, err := newConfig() 80 if err != nil { 81 return err 82 } 83 // acquire lock 84 if err := lock.TryLock(); err != nil { 85 return err 86 } 87 defer lock.Unlock() 88 89 // set the value 90 config.Set(path, value) 91 92 // write to the file 93 return ioutil.WriteFile(File, config.Bytes(), 0644) 94 } 95 96 func moveConfig(from, to string) error { 97 // read the config 98 b, err := ioutil.ReadFile(from) 99 if err != nil { 100 return fmt.Errorf("Failed to read config file %s: %v", from, err) 101 } 102 // remove the file 103 os.Remove(from) 104 105 // create new directory 106 dir := filepath.Dir(to) 107 if err := os.MkdirAll(dir, 0755); err != nil { 108 return fmt.Errorf("Failed to create dir %s: %v", dir, err) 109 } 110 // write the file to new location 111 return ioutil.WriteFile(to, b, 0644) 112 } 113 114 // newConfig returns a loaded config 115 func newConfig() (*conf.JSONValues, error) { 116 // check if the directory exists, otherwise create it 117 dir := filepath.Dir(File) 118 119 // for legacy purposes check if .micro is a file or directory 120 if f, err := os.Stat(dir); err != nil { 121 // check the error to see if the directory exists 122 if os.IsNotExist(err) { 123 if err := os.MkdirAll(dir, 0755); err != nil { 124 return nil, fmt.Errorf("Failed to create dir %s: %v", dir, err) 125 } 126 } else { 127 return nil, fmt.Errorf("Failed to create config dir %s: %v", dir, err) 128 } 129 } else { 130 // if not a directory, copy and move the config 131 if !f.IsDir() { 132 if err := moveConfig(dir, File); err != nil { 133 return nil, fmt.Errorf("Failed to move config from %s to %s: %v", dir, File, err) 134 } 135 } 136 } 137 138 // now write the file if it does not exist 139 if _, err := os.Stat(File); os.IsNotExist(err) { 140 ioutil.WriteFile(File, []byte(`{"env":"local"}`), 0644) 141 } else if err != nil { 142 return nil, fmt.Errorf("Failed to write config file %s: %v", File, err) 143 } 144 145 contents, err := ioutil.ReadFile(File) 146 if err != nil { 147 return nil, err 148 } 149 150 c := conf.NewJSONValues(contents) 151 152 // return the conf 153 return c, nil 154 } 155 156 func Path(paths ...string) string { 157 return strings.Join(paths, ".") 158 }