github.com/cnotch/ipchub@v1.1.0/config/provider.go (about) 1 // Copyright (c) Roman Atachiants and contributors. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for details. 3 // 4 // Copyright (c) 2019,CAOHONGJU All rights reserved. 5 // Use of this source code is governed by a MIT-style 6 // license that can be found in the LICENSE file. 7 8 package config 9 10 import ( 11 "errors" 12 "io" 13 "net/http" 14 "os" 15 "path/filepath" 16 "plugin" 17 "strings" 18 "time" 19 ) 20 21 // Provider 提供者接口 22 type Provider interface { 23 Name() string 24 Configure(config map[string]interface{}) error 25 } 26 27 // ProviderConfig 可扩展提供者配置 28 type ProviderConfig struct { 29 Provider string `json:"provider"` // 提供者类型 30 Config map[string]interface{} `json:"config,omitempty"` // 提供者配置 31 } 32 33 // Load 加载Provider 34 func (c *ProviderConfig) Load(builtins ...Provider) (Provider, error) { 35 for _, builtin := range builtins { 36 if strings.ToLower(builtin.Name()) == strings.ToLower(c.Provider) { 37 if err := builtin.Configure(c.Config); err != nil { 38 return nil, errors.New("The provider '" + c.Provider + "' could not be loaded. " + err.Error()) 39 } 40 41 return builtin, nil 42 } 43 } 44 45 // Attempt to load a plugin provider 46 p, err := plugin.Open(resolvePath(c.Provider)) 47 if err != nil { 48 return nil, errors.New("The provider plugin '" + c.Provider + "' could not be opened. " + err.Error()) 49 } 50 51 // Get the symbol 52 sym, err := p.Lookup("New") 53 if err != nil { 54 return nil, errors.New("The provider '" + c.Provider + "' does not contain 'func New() interface{}' symbol") 55 } 56 57 // Resolve the 58 pFactory, validFunc := sym.(*func() interface{}) 59 if !validFunc { 60 return nil, errors.New("The provider '" + c.Provider + "' does not contain 'func New() interface{}' symbol") 61 } 62 63 // Construct the provider 64 provider, validProv := ((*pFactory)()).(Provider) 65 if !validProv { 66 return nil, errors.New("The provider '" + c.Provider + "' does not implement 'Provider'") 67 } 68 69 // Configure the provider 70 err = provider.Configure(c.Config) 71 if err != nil { 72 return nil, errors.New("The provider '" + c.Provider + "' could not be configured") 73 } 74 75 // Succesfully opened and configured a provider 76 return provider, nil 77 } 78 79 // LoadOrPanic 加载 Provider 如果失败直接 panics. 80 func (c *ProviderConfig) LoadOrPanic(builtins ...Provider) Provider { 81 provider, err := c.Load(builtins...) 82 if err != nil { 83 panic(err) 84 } 85 86 return provider 87 } 88 89 // LoadProvider 加载Provider或Panic,默认值为第一个provider 90 func LoadProvider(config *ProviderConfig, providers ...Provider) Provider { 91 if config == nil || config.Provider == "" { 92 config = &ProviderConfig{ 93 Provider: providers[0].Name(), 94 } 95 } 96 97 // Load the provider according to the configuration 98 return config.LoadOrPanic(providers...) 99 } 100 101 func resolvePath(path string) string { 102 // If it's an url, download the file 103 if strings.HasPrefix(path, "http") { 104 f, err := httpFile(path) 105 if err != nil { 106 panic(err) 107 } 108 109 // Get the downloaded file path 110 path = f.Name() 111 } 112 113 // Make sure the path is absolute 114 path, _ = filepath.Abs(path) 115 return path 116 } 117 118 // DefaultClient used for http with a shorter timeout. 119 var defaultClient = &http.Client{ 120 Timeout: 5 * time.Second, 121 } 122 123 // httpFile downloads a file from HTTP 124 var httpFile = func(url string) (*os.File, error) { 125 tokens := strings.Split(url, "/") 126 fileName := tokens[len(tokens)-1] 127 128 output, err := os.Create(fileName) 129 if err != nil { 130 return nil, err 131 } 132 defer output.Close() 133 134 response, err := http.Get(url) 135 if err != nil { 136 return nil, err 137 } 138 defer response.Body.Close() 139 140 if _, err := io.Copy(output, response.Body); err != nil { 141 return nil, err 142 } 143 144 return output, nil 145 }