github.com/vicanso/pike@v1.0.1-0.20210630235453-9099e041f6ec/config/config.go (about) 1 // MIT License 2 3 // Copyright (c) 2020 Tree Xie 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in all 13 // copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 // SOFTWARE. 22 23 package config 24 25 import ( 26 "errors" 27 "strings" 28 29 "github.com/vicanso/pike/app" 30 "github.com/vicanso/pike/log" 31 "go.uber.org/zap" 32 "gopkg.in/yaml.v2" 33 ) 34 35 type ( 36 // Client client interface 37 Client interface { 38 // Get get the data of key 39 Get() (data []byte, err error) 40 // Set set the data of key 41 Set(data []byte) (err error) 42 // Watch watch change 43 Watch(OnChange) 44 // Close close client 45 Close() error 46 } 47 OnChange func() 48 49 // PikeConfig pike config 50 PikeConfig struct { 51 // YAML 界面展示之用,不需要保存 52 YAML string `json:"yaml,omitempty" yaml:"-"` 53 // Version 程序版本 54 Version string `json:"version,omitempty" yaml:"version,omitempty" ` 55 Admin AdminConfig `json:"admin,omitempty" yaml:"admin,omitempty" validate:"omitempty,dive"` 56 Compresses []CompressConfig `json:"compresses,omitempty" yaml:"compresses,omitempty" validate:"omitempty,dive"` 57 Caches []CacheConfig `json:"caches,omitempty" yaml:"caches,omitempty" validate:"omitempty,dive"` 58 Upstreams []UpstreamConfig `json:"upstreams,omitempty" yaml:"upstreams,omitempty" validate:"omitempty,dive"` 59 Locations []LocationConfig `json:"locations,omitempty" yaml:"locations,omitempty" validate:"omitempty,dive"` 60 Servers []ServerConfig `json:"servers,omitempty" yaml:"servers,omitempty" validate:"omitempty,dive"` 61 } 62 // AdminConfig admin config 63 AdminConfig struct { 64 User string `json:"user,omitempty" yaml:"user,omitempty" validate:"omitempty,min=3"` 65 Password string `json:"password,omitempty" yaml:"password,omitempty" validate:"omitempty,min=6"` 66 Remark string `json:"remark,omitempty" yaml:"remark,omitempty"` 67 } 68 // CompressConfig compress config 69 CompressConfig struct { 70 Name string `json:"name,omitempty" yaml:"name,omitempty" validate:"required,xName"` 71 Levels map[string]uint `json:"levels,omitempty" yaml:"levels,omitempty"` 72 Remark string `json:"remark,omitempty" yaml:"remark,omitempty"` 73 } 74 // CacheConfig cache config 75 CacheConfig struct { 76 Name string `json:"name,omitempty" yaml:"name,omitempty" validate:"required,xName"` 77 Size int `json:"size,omitempty" yaml:"size,omitempty" validate:"required,gt=0" ` 78 HitForPass string `json:"hitForPass,omitempty" yaml:"hitForPass,omitempty" validate:"required,xDuration"` 79 Store string `json:"store,omitempty" yaml:"store,omitempty" validate:"omitempty,url"` 80 Remark string `json:"remark,omitempty" yaml:"remark,omitempty"` 81 } 82 // UpstreamServerConfig upstream server config 83 UpstreamServerConfig struct { 84 Addr string `json:"addr,omitempty" yaml:"addr,omitempty" validate:"required,xAddr"` 85 Backup bool `json:"backup,omitempty" yaml:"backup,omitempty"` 86 // Healthy 界面展示使用,不需要保存 87 Healthy bool `json:"healthy,omitempty" yaml:"-"` 88 } 89 // UpstreamConfig upstream config 90 UpstreamConfig struct { 91 Name string `json:"name,omitempty" yaml:"name,omitempty" validate:"required,xName"` 92 HealthCheck string `json:"healthCheck,omitempty" yaml:"healthCheck,omitempty" validate:"omitempty,xURLPath"` 93 Policy string `json:"policy,omitempty" yaml:"policy,omitempty" validate:"omitempty,xPolicy"` 94 EnableH2C bool `json:"enableH2C,omitempty" yaml:"enableH2C,omitempty"` 95 AcceptEncoding string `json:"acceptEncoding,omitempty" yaml:"acceptEncoding,omitempty" validate:"omitempty,ascii"` 96 Servers []UpstreamServerConfig `json:"servers,omitempty" yaml:"servers,omitempty" validate:"required,dive"` 97 Remark string `json:"remark,omitempty" yaml:"remark,omitempty"` 98 } 99 // LocationConfig location config 100 LocationConfig struct { 101 Name string `json:"name,omitempty" yaml:"name,omitempty" validate:"required,xName"` 102 Upstream string `json:"upstream,omitempty" yaml:"upstream,omitempty" validate:"required,xName"` 103 Prefixes []string `json:"prefixes,omitempty" yaml:"prefixes,omitempty" validate:"omitempty,dive,xURLPath"` 104 Rewrites []string `json:"rewrites,omitempty" yaml:"rewrites,omitempty" validate:"omitempty,dive,xDivide"` 105 QueryStrings []string `json:"queryStrings,omitempty" yaml:"queryStrings,omitempty" validate:"omitempty,dive,xDivide"` 106 RespHeaders []string `json:"respHeaders,omitempty" yaml:"respHeaders,omitempty" validate:"omitempty,dive,xDivide"` 107 ReqHeaders []string `json:"reqHeaders,omitempty" yaml:"reqHeaders,omitempty" validate:"omitempty,dive,xDivide"` 108 Hosts []string `json:"hosts,omitempty" yaml:"hosts,omitempty" validate:"omitempty,dive,hostname"` 109 ProxyTimeout string `json:"proxyTimeout,omitempty" yaml:"proxyTimeout,omitempty" validate:"omitempty,xDuration"` 110 Remark string `json:"remark,omitempty" yaml:"remark,omitempty"` 111 } 112 // ServerConfig server config 113 ServerConfig struct { 114 LogFormat string `json:"logFormat,omitempty" yaml:"logFormat,omitempty"` 115 Addr string `json:"addr,omitempty" yaml:"addr,omitempty" validate:"required,ascii"` 116 Locations []string `json:"locations,omitempty" yaml:"locations,omitempty" validate:"required,dive,xName"` 117 Cache string `json:"cache,omitempty" yaml:"cache,omitempty" validate:"required,xName"` 118 Compress string `json:"compress,omitempty" yaml:"compress,omitempty" validate:"omitempty"` 119 // 最小压缩长度 120 CompressMinLength string `json:"compressMinLength,omitempty" yaml:"compressMinLength,omitempty" validate:"omitempty,xSize"` 121 // 压缩数据类型 122 CompressContentTypeFilter string `json:"compressContentTypeFilter,omitempty" yaml:"compressContentTypeFilter,omitempty" validate:"omitempty,xFilter"` 123 Remark string `json:"remark,omitempty" yaml:"remark,omitempty"` 124 } 125 ) 126 127 var defaultClient Client 128 129 var ( 130 ErrUpstreamNotFound = errors.New("upstream of location not found") 131 ErrLocationNotFound = errors.New("location of server not found") 132 ErrCacheNotFound = errors.New("cache of server not found") 133 ErrCompressNotFound = errors.New("compress of server not found") 134 ) 135 136 // InitDefaultClient init default client 137 func InitDefaultClient(url string) (err error) { 138 if defaultClient != nil { 139 // 如果关闭出错,仅输出日志 140 e := defaultClient.Close() 141 if e != nil { 142 log.Default().Error("close config client fail", 143 zap.Error(e), 144 ) 145 } 146 } 147 defaultClient = nil 148 if strings.HasPrefix(url, "etcd://") { 149 c, err := NewEtcdClient(url) 150 if err != nil { 151 return err 152 } 153 defaultClient = c 154 return nil 155 } 156 157 c, err := NewFileClient(url) 158 if err != nil { 159 return 160 } 161 defaultClient = c 162 return 163 } 164 165 func (c *PikeConfig) Validate() error { 166 err := defaultValidator.Struct(c) 167 if err != nil { 168 return err 169 } 170 // 判断location中设置的upstream是否存在 171 for _, l := range c.Locations { 172 found := false 173 for _, upstream := range c.Upstreams { 174 if l.Upstream == upstream.Name { 175 found = true 176 } 177 } 178 if !found { 179 return ErrUpstreamNotFound 180 } 181 } 182 // 校验server中的location, cache 以及 compress 是否正确设置 183 for _, s := range c.Servers { 184 for _, item := range s.Locations { 185 notFound := true 186 for _, l := range c.Locations { 187 if item == l.Name { 188 notFound = false 189 } 190 } 191 if notFound { 192 return ErrLocationNotFound 193 } 194 } 195 196 foundCache := s.Cache == "" 197 for _, cacheConfig := range c.Caches { 198 if cacheConfig.Name == s.Cache { 199 foundCache = true 200 } 201 } 202 if !foundCache { 203 return ErrCacheNotFound 204 } 205 206 foundCompress := s.Compress == "" 207 for _, compressConfig := range c.Compresses { 208 if compressConfig.Name == s.Compress { 209 foundCompress = true 210 } 211 } 212 if !foundCompress { 213 return ErrCompressNotFound 214 } 215 } 216 217 return nil 218 } 219 220 // GetAdminConfig get admin config 221 func (p *PikeConfig) GetAdminConfig() AdminConfig { 222 return p.Admin 223 } 224 225 // Read read pike config 226 func Read() (config *PikeConfig, err error) { 227 data, err := defaultClient.Get() 228 if err != nil { 229 return 230 } 231 config = &PikeConfig{} 232 err = yaml.Unmarshal(data, config) 233 if err != nil { 234 return 235 } 236 config.YAML = string(data) 237 return 238 } 239 240 // Write write pike config 241 func Write(config *PikeConfig) (err error) { 242 err = config.Validate() 243 if err != nil { 244 return 245 } 246 config.Version = app.GetVersion() 247 data, err := yaml.Marshal(config) 248 if err != nil { 249 return 250 } 251 return defaultClient.Set(data) 252 } 253 254 // Close close the client 255 func Close() (err error) { 256 if defaultClient != nil { 257 err = defaultClient.Close() 258 } 259 defaultClient = nil 260 return 261 } 262 263 // Watch watch the change 264 func Watch(onChange OnChange) { 265 defaultClient.Watch(onChange) 266 }