diff --git a/glide.lock b/glide.lock index e4cfa725e..a6eb38fcf 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 0f1c54f36d88f4625c048686f733842d09d48525b1cf84dc05d4acc698cfd86f -updated: 2017-04-11T17:02:21.540487905+02:00 +hash: 1aa32496b865dda72d76c7cba3458f1c2c467acf0b99aab4609323f109aa64f6 +updated: 2017-05-02T11:46:23.91434995-04:00 imports: - name: cloud.google.com/go version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c @@ -95,6 +95,8 @@ imports: subpackages: - Godeps/_workspace/src/github.com/coreos/go-systemd/journal - Godeps/_workspace/src/github.com/coreos/pkg/capnslog + - Godeps/_workspace/src/github.com/ugorji/go/codec + - Godeps/_workspace/src/golang.org/x/net/context - client - pkg/fileutil - pkg/pathutil @@ -199,7 +201,7 @@ imports: - name: github.com/fatih/color version: 9131ab34cf20d2f6d83fdc67168a5430d1c7dc23 - name: github.com/gambol99/go-marathon - version: 6b00a5b651b1beb2c6821863f7c60df490bd46c8 + version: d672c6fbb499596869d95146a26e7d0746c06c54 - name: github.com/ghodss/yaml version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee - name: github.com/go-ini/ini diff --git a/glide.yaml b/glide.yaml index b2fa274eb..daa8220de 100644 --- a/glide.yaml +++ b/glide.yaml @@ -91,7 +91,7 @@ import: - package: k8s.io/client-go version: v2.0.0 - package: github.com/gambol99/go-marathon - version: ^0.5.1 + version: d672c6fbb499596869d95146a26e7d0746c06c54 - package: github.com/ArthurHlt/go-eureka-client subpackages: - eureka diff --git a/vendor/github.com/gambol99/go-marathon/application.go b/vendor/github.com/gambol99/go-marathon/application.go index 573fc8183..62eb24705 100644 --- a/vendor/github.com/gambol99/go-marathon/application.go +++ b/vendor/github.com/gambol99/go-marathon/application.go @@ -56,43 +56,47 @@ type Port struct { // Application is the definition for an application in marathon type Application struct { - ID string `json:"id,omitempty"` - Cmd *string `json:"cmd,omitempty"` - Args *[]string `json:"args,omitempty"` - Constraints *[][]string `json:"constraints,omitempty"` - Container *Container `json:"container,omitempty"` - CPUs float64 `json:"cpus,omitempty"` - Disk *float64 `json:"disk,omitempty"` - Env *map[string]string `json:"env,omitempty"` - Executor *string `json:"executor,omitempty"` - HealthChecks *[]HealthCheck `json:"healthChecks,omitempty"` - Instances *int `json:"instances,omitempty"` - Mem *float64 `json:"mem,omitempty"` - Tasks []*Task `json:"tasks,omitempty"` - Ports []int `json:"ports"` - PortDefinitions *[]PortDefinition `json:"portDefinitions,omitempty"` - RequirePorts *bool `json:"requirePorts,omitempty"` - BackoffSeconds *float64 `json:"backoffSeconds,omitempty"` - BackoffFactor *float64 `json:"backoffFactor,omitempty"` - MaxLaunchDelaySeconds *float64 `json:"maxLaunchDelaySeconds,omitempty"` - TaskKillGracePeriodSeconds *float64 `json:"taskKillGracePeriodSeconds,omitempty"` - Deployments []map[string]string `json:"deployments,omitempty"` - Dependencies []string `json:"dependencies"` - TasksRunning int `json:"tasksRunning,omitempty"` - TasksStaged int `json:"tasksStaged,omitempty"` - TasksHealthy int `json:"tasksHealthy,omitempty"` - TasksUnhealthy int `json:"tasksUnhealthy,omitempty"` - TaskStats map[string]TaskStats `json:"taskStats,omitempty"` - User string `json:"user,omitempty"` - UpgradeStrategy *UpgradeStrategy `json:"upgradeStrategy,omitempty"` - Uris *[]string `json:"uris,omitempty"` - Version string `json:"version,omitempty"` - VersionInfo *VersionInfo `json:"versionInfo,omitempty"` - Labels *map[string]string `json:"labels,omitempty"` - AcceptedResourceRoles []string `json:"acceptedResourceRoles,omitempty"` - LastTaskFailure *LastTaskFailure `json:"lastTaskFailure,omitempty"` - Fetch *[]Fetch `json:"fetch,omitempty"` - IPAddressPerTask *IPAddressPerTask `json:"ipAddress,omitempty"` + ID string `json:"id,omitempty"` + Cmd *string `json:"cmd,omitempty"` + Args *[]string `json:"args,omitempty"` + Constraints *[][]string `json:"constraints,omitempty"` + Container *Container `json:"container,omitempty"` + CPUs float64 `json:"cpus,omitempty"` + GPUs *float64 `json:"gpus,omitempty"` + Disk *float64 `json:"disk,omitempty"` + Env *map[string]string `json:"env,omitempty"` + Executor *string `json:"executor,omitempty"` + HealthChecks *[]HealthCheck `json:"healthChecks,omitempty"` + ReadinessChecks *[]ReadinessCheck `json:"readinessChecks,omitempty"` + Instances *int `json:"instances,omitempty"` + Mem *float64 `json:"mem,omitempty"` + Tasks []*Task `json:"tasks,omitempty"` + Ports []int `json:"ports"` + PortDefinitions *[]PortDefinition `json:"portDefinitions,omitempty"` + RequirePorts *bool `json:"requirePorts,omitempty"` + BackoffSeconds *float64 `json:"backoffSeconds,omitempty"` + BackoffFactor *float64 `json:"backoffFactor,omitempty"` + MaxLaunchDelaySeconds *float64 `json:"maxLaunchDelaySeconds,omitempty"` + TaskKillGracePeriodSeconds *float64 `json:"taskKillGracePeriodSeconds,omitempty"` + Deployments []map[string]string `json:"deployments,omitempty"` + // Available when embedding readiness information through query parameter. + ReadinessCheckResults *[]ReadinessCheckResult `json:"readinessCheckResults,omitempty"` + Dependencies []string `json:"dependencies"` + TasksRunning int `json:"tasksRunning,omitempty"` + TasksStaged int `json:"tasksStaged,omitempty"` + TasksHealthy int `json:"tasksHealthy,omitempty"` + TasksUnhealthy int `json:"tasksUnhealthy,omitempty"` + TaskStats map[string]TaskStats `json:"taskStats,omitempty"` + User string `json:"user,omitempty"` + UpgradeStrategy *UpgradeStrategy `json:"upgradeStrategy,omitempty"` + Uris *[]string `json:"uris,omitempty"` + Version string `json:"version,omitempty"` + VersionInfo *VersionInfo `json:"versionInfo,omitempty"` + Labels *map[string]string `json:"labels,omitempty"` + AcceptedResourceRoles []string `json:"acceptedResourceRoles,omitempty"` + LastTaskFailure *LastTaskFailure `json:"lastTaskFailure,omitempty"` + Fetch *[]Fetch `json:"fetch,omitempty"` + IPAddressPerTask *IPAddressPerTask `json:"ipAddress,omitempty"` } // ApplicationVersions is a collection of application versions for a specific app in marathon @@ -180,6 +184,22 @@ func (r *Application) CPU(cpu float64) *Application { return r } +// SetGPUs set the amount of GPU per instance which is assigned to the application +// gpu: the GPU (check MESOS docs) per instance +func (r *Application) SetGPUs(gpu float64) *Application { + r.GPUs = &gpu + return r +} + +// EmptyGPUs explicitly empties GPUs -- use this if you need to empty +// gpus of an application that already has gpus set (setting port definitions to nil will +// keep the current value) +func (r *Application) EmptyGPUs() *Application { + g := 0.0 + r.GPUs = &g + return r +} + // Storage sets the amount of disk space the application is assigned, which for docker // application I don't believe is relevant // disk: the disk space in MB @@ -388,6 +408,26 @@ func (r *Application) HasHealthChecks() bool { return r.HealthChecks != nil && len(*r.HealthChecks) > 0 } +// AddReadinessCheck adds a readiness check. +func (r *Application) AddReadinessCheck(readinessCheck ReadinessCheck) *Application { + if r.ReadinessChecks == nil { + r.EmptyReadinessChecks() + } + + readinessChecks := *r.ReadinessChecks + readinessChecks = append(readinessChecks, readinessCheck) + r.ReadinessChecks = &readinessChecks + + return r +} + +// EmptyReadinessChecks empties the readiness checks. +func (r *Application) EmptyReadinessChecks() *Application { + r.ReadinessChecks = &[]ReadinessCheck{} + + return r +} + // DeploymentIDs retrieves the application deployments IDs func (r *Application) DeploymentIDs() []*DeploymentID { var deployments []*DeploymentID diff --git a/vendor/github.com/gambol99/go-marathon/client.go b/vendor/github.com/gambol99/go-marathon/client.go index 6ad34e4ab..b83d66711 100644 --- a/vendor/github.com/gambol99/go-marathon/client.go +++ b/vendor/github.com/gambol99/go-marathon/client.go @@ -174,14 +174,19 @@ type marathonClient struct { ipAddress string // the http server eventsHTTP *http.Server - // the http client use for making requests - httpClient *http.Client // the marathon hosts hosts *cluster // a map of service you wish to listen to listeners map[EventsChannel]EventsChannelContext // a custom logger for debug log messages debugLog *log.Logger + // the marathon HTTP client to ensure consistency in requests + client *httpClient +} + +type httpClient struct { + // the configuration for the marathon HTTP client + config Config } // NewClient creates a new marathon client @@ -197,8 +202,11 @@ func NewClient(config Config) (Marathon, error) { config.PollingWaitTime = defaultPollingWaitTime } + // step: setup shared client + client := &httpClient{config: config} + // step: create a new cluster - hosts, err := newCluster(config.HTTPClient, config.URL) + hosts, err := newCluster(client, config.URL, config.DCOSToken != "") if err != nil { return nil, err } @@ -209,11 +217,11 @@ func NewClient(config Config) (Marathon, error) { } return &marathonClient{ - config: config, - listeners: make(map[EventsChannel]EventsChannelContext), - hosts: hosts, - httpClient: config.HTTPClient, - debugLog: log.New(debugLogOutput, "", 0), + config: config, + listeners: make(map[EventsChannel]EventsChannelContext), + hosts: hosts, + debugLog: log.New(debugLogOutput, "", 0), + client: client, }, nil } @@ -246,34 +254,25 @@ func (r *marathonClient) apiDelete(uri string, post, result interface{}) error { return r.apiCall("DELETE", uri, post, result) } -func (r *marathonClient) apiCall(method, uri string, body, result interface{}) error { +func (r *marathonClient) apiCall(method, url string, body, result interface{}) error { for { - // step: grab a member from the cluster and attempt to perform the request - member, err := r.hosts.getMember() - if err != nil { - return ErrMarathonDown - } - - // step: Create the endpoint url - url := fmt.Sprintf("%s/%s", member, uri) - if r.config.DCOSToken != "" { - url = fmt.Sprintf("%s/%s", member+"/marathon", uri) - } - // step: marshall the request to json var requestBody []byte + var err error if body != nil { if requestBody, err = json.Marshal(body); err != nil { return err } } - // step: create the api request - request, err := r.buildAPIRequest(method, url, bytes.NewReader(requestBody)) + // step: create the API request + request, member, err := r.buildAPIRequest(method, url, bytes.NewReader(requestBody)) if err != nil { return err } - response, err := r.httpClient.Do(request) + + // step: perform the API request + response, err := r.client.Do(request) if err != nil { r.hosts.markDown(member) // step: attempt the request on another member @@ -318,20 +317,38 @@ func (r *marathonClient) apiCall(method, uri string, body, result interface{}) e } // buildAPIRequest creates a default API request -func (r *marathonClient) buildAPIRequest(method, url string, reader io.Reader) (*http.Request, error) { - // Make the http request to Marathon - request, err := http.NewRequest(method, url, reader) +func (r *marathonClient) buildAPIRequest(method, uri string, reader io.Reader) (request *http.Request, member string, err error) { + // Grab a member from the cluster + member, err = r.hosts.getMember() + if err != nil { + return nil, "", ErrMarathonDown + } + + // Build the HTTP request to Marathon + request, err = r.client.buildMarathonRequest(method, member, uri, reader) + if err != nil { + return nil, member, err + } + return request, member, nil +} + +func (rc *httpClient) buildMarathonRequest(method string, member string, uri string, reader io.Reader) (request *http.Request, err error) { + // Create the endpoint URL + url := fmt.Sprintf("%s/%s", member, uri) + + // Instantiate an HTTP request + request, err = http.NewRequest(method, url, reader) if err != nil { return nil, err } // Add any basic auth and the content headers - if r.config.HTTPBasicAuthUser != "" && r.config.HTTPBasicPassword != "" { - request.SetBasicAuth(r.config.HTTPBasicAuthUser, r.config.HTTPBasicPassword) + if rc.config.HTTPBasicAuthUser != "" && rc.config.HTTPBasicPassword != "" { + request.SetBasicAuth(rc.config.HTTPBasicAuthUser, rc.config.HTTPBasicPassword) } - if r.config.DCOSToken != "" { - request.Header.Add("Authorization", "token="+r.config.DCOSToken) + if rc.config.DCOSToken != "" { + request.Header.Add("Authorization", "token="+rc.config.DCOSToken) } request.Header.Add("Content-Type", "application/json") @@ -340,6 +357,10 @@ func (r *marathonClient) buildAPIRequest(method, url string, reader io.Reader) ( return request, nil } +func (rc *httpClient) Do(request *http.Request) (response *http.Response, err error) { + return rc.config.HTTPClient.Do(request) +} + var oneLogLineRegex = regexp.MustCompile(`(?m)^\s*`) // oneLogLine removes indentation at the beginning of each line and diff --git a/vendor/github.com/gambol99/go-marathon/cluster.go b/vendor/github.com/gambol99/go-marathon/cluster.go index 670b6da1e..c7ea0a429 100644 --- a/vendor/github.com/gambol99/go-marathon/cluster.go +++ b/vendor/github.com/gambol99/go-marathon/cluster.go @@ -18,7 +18,6 @@ package marathon import ( "fmt" - "net/http" "net/url" "strings" "sync" @@ -38,8 +37,8 @@ type cluster struct { sync.RWMutex // a collection of nodes members []*member - // the http client - client *http.Client + // the marathon HTTP client to ensure consistency in requests + client *httpClient } // member represents an individual endpoint @@ -51,7 +50,7 @@ type member struct { } // newCluster returns a new marathon cluster -func newCluster(client *http.Client, marathonURL string) (*cluster, error) { +func newCluster(client *httpClient, marathonURL string, isDCOS bool) (*cluster, error) { // step: extract and basic validate the endpoints var members []*member var defaultProto string @@ -83,6 +82,13 @@ func newCluster(client *http.Client, marathonURL string) (*cluster, error) { return nil, newInvalidEndpointError("endpoint: %s must have a host", endpoint) } + // step: if DCOS is set and no path is given, set the default DCOS path. + // done in order to maintain compatibility with automatic addition of the + // default DCOS path. + if isDCOS && strings.TrimLeft(u.Path, "/") == "" { + u.Path = defaultDCOSPath + } + // step: create a new node for this endpoint members = append(members, &member{endpoint: u.String()}) } @@ -125,9 +131,12 @@ func (c *cluster) markDown(endpoint string) { func (c *cluster) healthCheckNode(node *member) { // step: wait for the node to become active ... we are assuming a /ping is enough here for { - res, err := c.client.Get(fmt.Sprintf("%s/ping", node.endpoint)) - if err == nil && res.StatusCode == 200 { - break + req, err := c.client.buildMarathonRequest("GET", node.endpoint, "/ping", nil) + if err == nil { + res, err := c.client.Do(req) + if err == nil && res.StatusCode == 200 { + break + } } <-time.After(time.Duration(5 * time.Second)) } diff --git a/vendor/github.com/gambol99/go-marathon/config.go b/vendor/github.com/gambol99/go-marathon/config.go index a0ede2814..67bba0982 100644 --- a/vendor/github.com/gambol99/go-marathon/config.go +++ b/vendor/github.com/gambol99/go-marathon/config.go @@ -25,6 +25,8 @@ import ( const defaultPollingWaitTime = 500 * time.Millisecond +const defaultDCOSPath = "marathon" + // EventsTransport describes which transport should be used to deliver Marathon events type EventsTransport int diff --git a/vendor/github.com/gambol99/go-marathon/deployment.go b/vendor/github.com/gambol99/go-marathon/deployment.go index 7e2bddd16..7d57f1758 100644 --- a/vendor/github.com/gambol99/go-marathon/deployment.go +++ b/vendor/github.com/gambol99/go-marathon/deployment.go @@ -42,8 +42,9 @@ type DeploymentID struct { // DeploymentStep is a step in the application deployment plan type DeploymentStep struct { - Action string `json:"action"` - App string `json:"app"` + Action string `json:"action"` + App string `json:"app"` + ReadinessCheckResults *[]ReadinessCheckResult `json:"readinessCheckResults,omitempty"` } // StepActions is a series of deployment steps diff --git a/vendor/github.com/gambol99/go-marathon/events.go b/vendor/github.com/gambol99/go-marathon/events.go index 5a68985ac..f97df9084 100644 --- a/vendor/github.com/gambol99/go-marathon/events.go +++ b/vendor/github.com/gambol99/go-marathon/events.go @@ -273,9 +273,10 @@ type EventGroupChangeFailed struct { // EventDeploymentSuccess describes a 'deployment_success' event. type EventDeploymentSuccess struct { - ID string `json:"id"` - EventType string `json:"eventType"` - Timestamp string `json:"timestamp"` + ID string `json:"id"` + EventType string `json:"eventType"` + Timestamp string `json:"timestamp"` + Plan *DeploymentPlan `json:"plan"` } // EventDeploymentFailed describes a 'deployment_failed' event. diff --git a/vendor/github.com/gambol99/go-marathon/health.go b/vendor/github.com/gambol99/go-marathon/health.go index 8810213b4..c264416b0 100644 --- a/vendor/github.com/gambol99/go-marathon/health.go +++ b/vendor/github.com/gambol99/go-marathon/health.go @@ -27,6 +27,7 @@ type HealthCheck struct { GracePeriodSeconds int `json:"gracePeriodSeconds,omitempty"` IntervalSeconds int `json:"intervalSeconds,omitempty"` TimeoutSeconds int `json:"timeoutSeconds,omitempty"` + IgnoreHTTP1xx *bool `json:"ignoreHttp1xx,ommitempty"` } // SetCommand sets the given command on the health check. @@ -59,6 +60,12 @@ func (h HealthCheck) SetMaxConsecutiveFailures(i int) HealthCheck { return h } +// SetIgnoreHTTP1xx sets ignore http 1xx on the health check. +func (h HealthCheck) SetIgnoreHTTP1xx(ignore bool) HealthCheck { + h.IgnoreHTTP1xx = &ignore + return h +} + // NewDefaultHealthCheck creates a default application health check func NewDefaultHealthCheck() *HealthCheck { portIndex := 0 diff --git a/vendor/github.com/gambol99/go-marathon/readiness.go b/vendor/github.com/gambol99/go-marathon/readiness.go new file mode 100644 index 000000000..c1887c3c3 --- /dev/null +++ b/vendor/github.com/gambol99/go-marathon/readiness.go @@ -0,0 +1,99 @@ +/* +Copyright 2017 Rohith All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package marathon + +import "time" + +// ReadinessCheck represents a readiness check. +type ReadinessCheck struct { + Name *string `json:"name,omitempty"` + Protocol string `json:"protocol,omitempty"` + Path string `json:"path,omitempty"` + PortName string `json:"portName,omitempty"` + IntervalSeconds int `json:"intervalSeconds,omitempty"` + TimeoutSeconds int `json:"timeoutSeconds,omitempty"` + HTTPStatusCodesForReady *[]int `json:"httpStatusCodesForReady,omitempty"` + PreserveLastResponse *bool `json:"preserveLastResponse,omitempty"` +} + +// SetName sets the name on the readiness check. +func (rc *ReadinessCheck) SetName(name string) *ReadinessCheck { + rc.Name = &name + return rc +} + +// SetProtocol sets the protocol on the readiness check. +func (rc *ReadinessCheck) SetProtocol(proto string) *ReadinessCheck { + rc.Protocol = proto + return rc +} + +// SetPath sets the path on the readiness check. +func (rc *ReadinessCheck) SetPath(p string) *ReadinessCheck { + rc.Path = p + return rc +} + +// SetPortName sets the port name on the readiness check. +func (rc *ReadinessCheck) SetPortName(name string) *ReadinessCheck { + rc.PortName = name + return rc +} + +// SetInterval sets the interval on the readiness check. +func (rc *ReadinessCheck) SetInterval(interval time.Duration) *ReadinessCheck { + secs := int(interval.Seconds()) + rc.IntervalSeconds = secs + return rc +} + +// SetTimeout sets the timeout on the readiness check. +func (rc *ReadinessCheck) SetTimeout(timeout time.Duration) *ReadinessCheck { + secs := int(timeout.Seconds()) + rc.TimeoutSeconds = secs + return rc +} + +// SetHTTPStatusCodesForReady sets the HTTP status codes for ready on the +// readiness check. +func (rc *ReadinessCheck) SetHTTPStatusCodesForReady(codes []int) *ReadinessCheck { + rc.HTTPStatusCodesForReady = &codes + return rc +} + +// SetPreserveLastResponse sets the preserve last response flag on the +// readiness check. +func (rc *ReadinessCheck) SetPreserveLastResponse(preserve bool) *ReadinessCheck { + rc.PreserveLastResponse = &preserve + return rc +} + +// ReadinessLastResponse holds the result of the last response embedded in a +// readiness check result. +type ReadinessLastResponse struct { + Body string `json:"body"` + ContentType string `json:"contentType"` + Status int `json:"status"` +} + +// ReadinessCheckResult is the result of a readiness check. +type ReadinessCheckResult struct { + Name string `json:"name"` + TaskID string `json:"taskId"` + Ready bool `json:"ready"` + LastResponse ReadinessLastResponse `json:"lastResponse,omitempty"` +} diff --git a/vendor/github.com/gambol99/go-marathon/subscription.go b/vendor/github.com/gambol99/go-marathon/subscription.go index c84b76998..f1c7e25d6 100644 --- a/vendor/github.com/gambol99/go-marathon/subscription.go +++ b/vendor/github.com/gambol99/go-marathon/subscription.go @@ -167,19 +167,14 @@ func (r *marathonClient) registerSSESubscription() error { if r.subscribedToSSE { return nil } - // Get a member from the cluster - marathon, err := r.hosts.getMember() - if err != nil { - return err - } - request, err := r.buildAPIRequest("GET", fmt.Sprintf("%s/%s", marathon, marathonAPIEventStream), nil) + request, _, err := r.buildAPIRequest("GET", marathonAPIEventStream, nil) if err != nil { return err } // Try to connect to stream, reusing the http client settings - stream, err := eventsource.SubscribeWith("", r.httpClient, request) + stream, err := eventsource.SubscribeWith("", r.config.HTTPClient, request) if err != nil { return err }