vitess.io/vitess@v0.16.2/go/vt/vtadmin/vtsql/config.go (about)

     1  /*
     2  Copyright 2020 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package vtsql
    18  
    19  import (
    20  	"database/sql"
    21  	"fmt"
    22  
    23  	"github.com/spf13/pflag"
    24  
    25  	"vitess.io/vitess/go/vt/grpcclient"
    26  	"vitess.io/vitess/go/vt/vitessdriver"
    27  	"vitess.io/vitess/go/vt/vtadmin/cluster/discovery"
    28  	"vitess.io/vitess/go/vt/vtadmin/cluster/resolver"
    29  	"vitess.io/vitess/go/vt/vtadmin/credentials"
    30  
    31  	vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin"
    32  )
    33  
    34  // Config represents the options that modify the behavior of a vtqsl.VTGateProxy.
    35  type Config struct {
    36  	Credentials Credentials
    37  	// CredentialsPath is used only to power vtadmin debug endpoints; there may
    38  	// be a better way where we don't need to put this in the config, because
    39  	// it's not really an "option" in normal use.
    40  	CredentialsPath string
    41  
    42  	Cluster         *vtadminpb.Cluster
    43  	ResolverOptions *resolver.Options
    44  
    45  	dialFunc func(c vitessdriver.Configuration) (*sql.DB, error)
    46  }
    47  
    48  // ConfigOption is a function that mutates a Config. It should return the same
    49  // Config structure, in a builder-pattern style.
    50  type ConfigOption func(cfg *Config) *Config
    51  
    52  // WithDialFunc returns a ConfigOption that applies the given dial function to
    53  // a Config.
    54  //
    55  // It is used to support dependency injection in tests, and needs to be exported
    56  // for higher-level tests (for example, package vtadmin/cluster).
    57  func WithDialFunc(f func(c vitessdriver.Configuration) (*sql.DB, error)) ConfigOption {
    58  	return func(cfg *Config) *Config {
    59  		cfg.dialFunc = f
    60  		return cfg
    61  	}
    62  }
    63  
    64  // Parse returns a new config with the given cluster ID and name, after
    65  // attempting to parse the command-line pflags into that Config. See
    66  // (*Config).Parse() for more details.
    67  func Parse(cluster *vtadminpb.Cluster, disco discovery.Discovery, args []string) (*Config, error) {
    68  	cfg := &Config{
    69  		Cluster: cluster,
    70  		ResolverOptions: &resolver.Options{
    71  			Discovery: disco,
    72  		},
    73  	}
    74  
    75  	err := cfg.Parse(args)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	return cfg, nil
    81  }
    82  
    83  // Parse reads options specified as command-line pflags (--key=value, note the
    84  // double-dash!) into a vtsql.Config. It is meant to be called from
    85  // (*cluster.Cluster).New().
    86  func (c *Config) Parse(args []string) error {
    87  	fs := pflag.NewFlagSet("", pflag.ContinueOnError)
    88  
    89  	if c.ResolverOptions == nil {
    90  		c.ResolverOptions = &resolver.Options{}
    91  	}
    92  
    93  	c.ResolverOptions.InstallFlags(fs)
    94  
    95  	credentialsTmplStr := fs.String("credentials-path-tmpl", "",
    96  		"Go template used to specify a path to a credentials file, which is a json file containing "+
    97  			"a Username and Password. Templates are given the context of the vtsql.Config, and primarily "+
    98  			"interoplate the cluster name and ID variables.")
    99  	effectiveUser := fs.String("effective-user", "", "username to send queries on behalf of")
   100  
   101  	if err := fs.Parse(args); err != nil {
   102  		return err
   103  	}
   104  
   105  	var creds *grpcclient.StaticAuthClientCreds
   106  
   107  	if *credentialsTmplStr != "" {
   108  		_creds, path, err := credentials.LoadFromTemplate(*credentialsTmplStr, c)
   109  		if err != nil {
   110  			return fmt.Errorf("cannot load credentials from path template %s: %w", *credentialsTmplStr, err)
   111  		}
   112  
   113  		c.CredentialsPath = path
   114  		creds = _creds
   115  	}
   116  
   117  	if creds != nil {
   118  		// If we did not receive an effective user, but loaded credentials, then the
   119  		// immediate user is the effective user.
   120  		if *effectiveUser == "" {
   121  			*effectiveUser = creds.Username
   122  		}
   123  
   124  		c.Credentials = &StaticAuthCredentials{
   125  			EffectiveUser:         *effectiveUser,
   126  			StaticAuthClientCreds: creds,
   127  		}
   128  	}
   129  
   130  	return nil
   131  }