github.hscsec.cn/hashicorp/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 }