1package structs 2 3import ( 4 "fmt" 5 "reflect" 6 "time" 7 8 "github.com/hashicorp/consul/lib" 9 "github.com/hashicorp/consul/types" 10) 11 12type CheckTypes []*CheckType 13 14// CheckType is used to create either the CheckMonitor or the CheckTTL. 15// The following types are supported: Script, HTTP, TCP, Docker, TTL, GRPC, Alias, H2PING. Script, 16// HTTP, Docker, TCP, GRPC, and H2PING all require Interval. Only one of the types may 17// to be provided: TTL or Script/Interval or HTTP/Interval or TCP/Interval or 18// Docker/Interval or GRPC/Interval or AliasService or H2PING/Interval. 19// Since types like CheckHTTP and CheckGRPC derive from CheckType, there are 20// helper conversion methods that do the reverse conversion. ie. checkHTTP.CheckType() 21type CheckType struct { 22 // fields already embedded in CheckDefinition 23 // Note: CheckType.CheckID == CheckDefinition.ID 24 25 CheckID types.CheckID 26 Name string 27 Status string 28 Notes string 29 30 // fields copied to CheckDefinition 31 // Update CheckDefinition when adding fields here 32 33 ScriptArgs []string 34 HTTP string 35 H2PING string 36 Header map[string][]string 37 Method string 38 Body string 39 TCP string 40 Interval time.Duration 41 AliasNode string 42 AliasService string 43 DockerContainerID string 44 Shell string 45 GRPC string 46 GRPCUseTLS bool 47 TLSServerName string 48 TLSSkipVerify bool 49 Timeout time.Duration 50 TTL time.Duration 51 SuccessBeforePassing int 52 FailuresBeforeCritical int 53 54 // Definition fields used when exposing checks through a proxy 55 ProxyHTTP string 56 ProxyGRPC string 57 58 // DeregisterCriticalServiceAfter, if >0, will cause the associated 59 // service, if any, to be deregistered if this check is critical for 60 // longer than this duration. 61 DeregisterCriticalServiceAfter time.Duration 62 OutputMaxSize int 63} 64 65func (t *CheckType) UnmarshalJSON(data []byte) (err error) { 66 type Alias CheckType 67 aux := &struct { 68 Interval interface{} 69 Timeout interface{} 70 TTL interface{} 71 DeregisterCriticalServiceAfter interface{} 72 73 // Translate fields 74 75 // "args" -> ScriptArgs 76 Args []string `json:"args"` 77 ScriptArgsSnake []string `json:"script_args"` 78 DeregisterCriticalServiceAfterSnake interface{} `json:"deregister_critical_service_after"` 79 DockerContainerIDSnake string `json:"docker_container_id"` 80 TLSServerNameSnake string `json:"tls_server_name"` 81 TLSSkipVerifySnake bool `json:"tls_skip_verify"` 82 GRPCUseTLSSnake bool `json:"grpc_use_tls"` 83 84 // These are going to be ignored but since we are disallowing unknown fields 85 // during parsing we have to be explicit about parsing but not using these. 86 ServiceID string `json:"ServiceID"` 87 ServiceIDSnake string `json:"service_id"` 88 89 *Alias 90 }{ 91 Alias: (*Alias)(t), 92 } 93 if err = lib.UnmarshalJSON(data, aux); err != nil { 94 return err 95 } 96 if aux.DeregisterCriticalServiceAfter == nil { 97 aux.DeregisterCriticalServiceAfter = aux.DeregisterCriticalServiceAfterSnake 98 } 99 if len(t.ScriptArgs) == 0 { 100 t.ScriptArgs = aux.Args 101 } 102 if len(t.ScriptArgs) == 0 { 103 t.ScriptArgs = aux.ScriptArgsSnake 104 } 105 if t.DockerContainerID == "" { 106 t.DockerContainerID = aux.DockerContainerIDSnake 107 } 108 if t.TLSServerName == "" { 109 t.TLSServerName = aux.TLSServerNameSnake 110 } 111 if aux.TLSSkipVerifySnake { 112 t.TLSSkipVerify = aux.TLSSkipVerifySnake 113 } 114 if aux.GRPCUseTLSSnake { 115 t.GRPCUseTLS = aux.GRPCUseTLSSnake 116 } 117 118 if aux.Interval != nil { 119 switch v := aux.Interval.(type) { 120 case string: 121 if t.Interval, err = time.ParseDuration(v); err != nil { 122 return err 123 } 124 case float64: 125 t.Interval = time.Duration(v) 126 } 127 } 128 if aux.Timeout != nil { 129 switch v := aux.Timeout.(type) { 130 case string: 131 if t.Timeout, err = time.ParseDuration(v); err != nil { 132 return err 133 } 134 case float64: 135 t.Timeout = time.Duration(v) 136 } 137 } 138 if aux.TTL != nil { 139 switch v := aux.TTL.(type) { 140 case string: 141 if t.TTL, err = time.ParseDuration(v); err != nil { 142 return err 143 } 144 case float64: 145 t.TTL = time.Duration(v) 146 } 147 } 148 if aux.DeregisterCriticalServiceAfter != nil { 149 switch v := aux.DeregisterCriticalServiceAfter.(type) { 150 case string: 151 if t.DeregisterCriticalServiceAfter, err = time.ParseDuration(v); err != nil { 152 return err 153 } 154 case float64: 155 t.DeregisterCriticalServiceAfter = time.Duration(v) 156 } 157 } 158 159 return nil 160 161} 162 163// Validate returns an error message if the check is invalid 164func (c *CheckType) Validate() error { 165 intervalCheck := c.IsScript() || c.HTTP != "" || c.TCP != "" || c.GRPC != "" || c.H2PING != "" 166 167 if c.Interval > 0 && c.TTL > 0 { 168 return fmt.Errorf("Interval and TTL cannot both be specified") 169 } 170 if intervalCheck && c.Interval <= 0 { 171 return fmt.Errorf("Interval must be > 0 for Script, HTTP, H2PING, or TCP checks") 172 } 173 if intervalCheck && c.IsAlias() { 174 return fmt.Errorf("Interval cannot be set for Alias checks") 175 } 176 if c.IsAlias() && c.TTL > 0 { 177 return fmt.Errorf("TTL must be not be set for Alias checks") 178 } 179 if !intervalCheck && !c.IsAlias() && c.TTL <= 0 { 180 return fmt.Errorf("TTL must be > 0 for TTL checks") 181 } 182 if c.OutputMaxSize < 0 { 183 return fmt.Errorf("MaxOutputMaxSize must be positive") 184 } 185 return nil 186} 187 188// Empty checks if the CheckType has no fields defined. Empty checks parsed from json configs are filtered out 189func (c *CheckType) Empty() bool { 190 return reflect.DeepEqual(c, &CheckType{}) 191} 192 193// IsAlias checks if this is an alias check. 194func (c *CheckType) IsAlias() bool { 195 return c.AliasNode != "" || c.AliasService != "" 196} 197 198// IsScript checks if this is a check that execs some kind of script. 199func (c *CheckType) IsScript() bool { 200 return len(c.ScriptArgs) > 0 201} 202 203// IsTTL checks if this is a TTL type 204func (c *CheckType) IsTTL() bool { 205 return c.TTL > 0 206} 207 208// IsMonitor checks if this is a Monitor type 209func (c *CheckType) IsMonitor() bool { 210 return c.IsScript() && c.DockerContainerID == "" && c.Interval > 0 211} 212 213// IsHTTP checks if this is a HTTP type 214func (c *CheckType) IsHTTP() bool { 215 return c.HTTP != "" && c.Interval > 0 216} 217 218// IsTCP checks if this is a TCP type 219func (c *CheckType) IsTCP() bool { 220 return c.TCP != "" && c.Interval > 0 221} 222 223// IsDocker returns true when checking a docker container. 224func (c *CheckType) IsDocker() bool { 225 return c.IsScript() && c.DockerContainerID != "" && c.Interval > 0 226} 227 228// IsGRPC checks if this is a GRPC type 229func (c *CheckType) IsGRPC() bool { 230 return c.GRPC != "" && c.Interval > 0 231} 232 233// IsH2PING checks if this is a H2PING type 234func (c *CheckType) IsH2PING() bool { 235 return c.H2PING != "" && c.Interval > 0 236} 237 238func (c *CheckType) Type() string { 239 switch { 240 case c.IsGRPC(): 241 return "grpc" 242 case c.IsHTTP(): 243 return "http" 244 case c.IsTTL(): 245 return "ttl" 246 case c.IsTCP(): 247 return "tcp" 248 case c.IsAlias(): 249 return "alias" 250 case c.IsDocker(): 251 return "docker" 252 case c.IsScript(): 253 return "script" 254 case c.IsH2PING(): 255 return "h2ping" 256 default: 257 return "" 258 } 259} 260