1/* 2** Zabbix 3** Copyright (C) 2001-2021 Zabbix SIA 4** 5** This program is free software; you can redistribute it and/or modify 6** it under the terms of the GNU General Public License as published by 7** the Free Software Foundation; either version 2 of the License, or 8** (at your option) any later version. 9** 10** This program is distributed in the hope that it will be useful, 11** but WITHOUT ANY WARRANTY; without even the implied warranty of 12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13** GNU General Public License for more details. 14** 15** You should have received a copy of the GNU General Public License 16** along with this program; if not, write to the Free Software 17** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18**/ 19 20package postgres 21 22import ( 23 "context" 24 "net/http" 25 "net/url" 26 "time" 27 28 "zabbix.com/pkg/tlsconfig" 29 "zabbix.com/pkg/uri" 30 "zabbix.com/pkg/zbxerr" 31 32 "github.com/omeid/go-yarn" 33 34 "zabbix.com/pkg/plugin" 35) 36 37const ( 38 pluginName = "Postgres" 39 sqlExt = ".sql" 40 hkInterval = 10 41) 42 43// Plugin inherits plugin.Base and store plugin-specific data. 44type Plugin struct { 45 plugin.Base 46 connMgr *ConnManager 47 options PluginOptions 48} 49 50// impl is the pointer to the plugin implementation. 51var impl Plugin 52 53// Export implements the Exporter interface. 54func (p *Plugin) Export(key string, rawParams []string, _ plugin.ContextProvider) (result interface{}, err error) { 55 var extraParams []string 56 57 params, err := metrics[key].EvalParams(rawParams, p.options.Sessions) 58 if err != nil { 59 return nil, err 60 } 61 62 details, err := tlsconfig.CreateDetails(params["sessionName"], params["TLSConnect"], 63 params["TLSCAFile"], params["TLSCertFile"], params["TLSKeyFile"], params["URI"]) 64 if err != nil { 65 return nil, zbxerr.ErrorInvalidConfiguration.Wrap(err) 66 } 67 68 dbname := url.QueryEscape(params["Database"]) 69 70 uri, err := uri.NewWithCreds(params["URI"]+"?dbname="+dbname, params["User"], params["Password"], uriDefaults) 71 if err != nil { 72 return nil, err 73 } 74 75 if len(rawParams) > len(params) { 76 extraParams = rawParams[len(params):] 77 } 78 79 handleMetric := getHandlerFunc(key) 80 if handleMetric == nil { 81 return nil, zbxerr.ErrorUnsupportedMetric 82 } 83 84 conn, err := p.connMgr.GetConnection(*uri, details) 85 if err != nil { 86 // Special logic of processing connection errors should be used if pgsql.ping is requested 87 // because it must return pingFailed if any error occurred. 88 if key == keyPing { 89 return pingFailed, nil 90 } 91 92 p.Errf(err.Error()) 93 94 return nil, err 95 } 96 97 ctx, cancel := context.WithTimeout(conn.ctx, conn.callTimeout) 98 defer cancel() 99 100 result, err = handleMetric(ctx, conn, key, params, extraParams...) 101 102 if err != nil { 103 p.Errf(err.Error()) 104 } 105 106 return result, err 107} 108 109// Start implements the Runner interface and performs initialization when plugin is activated. 110func (p *Plugin) Start() { 111 queryStorage, err := yarn.New(http.Dir(p.options.CustomQueriesPath), "*"+sqlExt) 112 if err != nil { 113 p.Errf(err.Error()) 114 // create empty storage if error occurred 115 queryStorage = yarn.NewFromMap(map[string]string{}) 116 } 117 118 p.connMgr = NewConnManager( 119 time.Duration(p.options.KeepAlive)*time.Second, 120 time.Duration(p.options.Timeout)*time.Second, 121 time.Duration(p.options.CallTimeout)*time.Second, 122 hkInterval*time.Second, 123 queryStorage, 124 ) 125} 126 127// Stop implements the Runner interface and frees resources when plugin is deactivated. 128func (p *Plugin) Stop() { 129 p.connMgr.Destroy() 130 p.connMgr = nil 131} 132