github.com/akzi/consul@v1.4.5/command/services/config.go (about)

     1  package services
     2  
     3  import (
     4  	"reflect"
     5  	"time"
     6  
     7  	"github.com/hashicorp/consul/agent/config"
     8  	"github.com/hashicorp/consul/agent/structs"
     9  	"github.com/hashicorp/consul/api"
    10  	"github.com/mitchellh/mapstructure"
    11  )
    12  
    13  // ServicesFromFiles returns the list of agent service registration structs
    14  // from a set of file arguments.
    15  func ServicesFromFiles(files []string) ([]*api.AgentServiceRegistration, error) {
    16  	// We set devMode to true so we can get the basic valid default
    17  	// configuration. devMode doesn't set any services by default so this
    18  	// is okay since we only look at services.
    19  	devMode := true
    20  	b, err := config.NewBuilder(config.Flags{
    21  		ConfigFiles: files,
    22  		DevMode:     &devMode,
    23  	})
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	cfg, err := b.BuildAndValidate()
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  
    33  	// The services are now in "structs.ServiceDefinition" form and we need
    34  	// them in "api.AgentServiceRegistration" form so do the conversion.
    35  	result := make([]*api.AgentServiceRegistration, 0, len(cfg.Services))
    36  	for _, svc := range cfg.Services {
    37  		apiSvc, err := serviceToAgentService(svc)
    38  		if err != nil {
    39  			return nil, err
    40  		}
    41  
    42  		result = append(result, apiSvc)
    43  	}
    44  
    45  	return result, nil
    46  }
    47  
    48  // serviceToAgentService converts a ServiceDefinition struct to an
    49  // AgentServiceRegistration API struct.
    50  func serviceToAgentService(svc *structs.ServiceDefinition) (*api.AgentServiceRegistration, error) {
    51  	// mapstructure can do this for us, but we encapsulate it in this
    52  	// helper function in case we need to change the logic in the future.
    53  	var result api.AgentServiceRegistration
    54  	d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
    55  		Result:           &result,
    56  		DecodeHook:       timeDurationToStringHookFunc(),
    57  		WeaklyTypedInput: true,
    58  	})
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	if err := d.Decode(svc); err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	// The structs version has non-pointer checks and the destination
    67  	// has pointers, so we need to set the destination to nil if there
    68  	// is no check ID set.
    69  	if result.Check != nil && result.Check.Name == "" {
    70  		result.Check = nil
    71  	}
    72  	if len(result.Checks) == 1 && result.Checks[0].Name == "" {
    73  		result.Checks = nil
    74  	}
    75  
    76  	return &result, nil
    77  }
    78  
    79  // timeDurationToStringHookFunc returns a DecodeHookFunc that converts
    80  // time.Duration to string.
    81  func timeDurationToStringHookFunc() mapstructure.DecodeHookFunc {
    82  	return func(
    83  		f reflect.Type,
    84  		t reflect.Type,
    85  		data interface{}) (interface{}, error) {
    86  		dur, ok := data.(time.Duration)
    87  		if !ok {
    88  			return data, nil
    89  		}
    90  		if t.Kind() != reflect.String {
    91  			return data, nil
    92  		}
    93  		if dur == 0 {
    94  			return "", nil
    95  		}
    96  
    97  		// Convert it by parsing
    98  		return data.(time.Duration).String(), nil
    99  	}
   100  }