func (r *marathonClient) handleMarathonEvent(writer http.ResponseWriter, request *http.Request) { body, err := ioutil.ReadAll(request.Body) if err != nil { return } // step: process the event and decode the event content := string(body[:]) eventType := new(EventType) err = json.NewDecoder(strings.NewReader(content)).Decode(eventType) if err != nil { glog.V(DEBUG_LEVEL).Infof("failed to decode the event type, content: %s, error: %s", content, err) return } // step: check the type is handled event, err := r.GetEvent(eventType.EventType) if err != nil { glog.V(DEBUG_LEVEL).Infof("unable to retrieve the event, type: %s", eventType.EventType) return } // step: lets decode message err = json.NewDecoder(strings.NewReader(content)).Decode(event.Event) if err != nil { glog.V(DEBUG_LEVEL).Infof("failed to decode the event type: %d, name: %s error: %s", event.ID, err) return } r.RLock() defer r.RUnlock() // step: check if anyone is listen for this event for channel, filter := range r.listeners { // step: check if this listener wants this event type if event.ID&filter != 0 { go func(ch EventsChannel, e *Event) { ch <- e }(channel, event) } } }
func (r *marathonClient) apiCall(method, uri, body string, result interface{}) (int, string, error) { glog.V(DEBUG_LEVEL).Infof("[api]: method: %s, uri: %s, body: %s", method, uri, body) status, content, _, err := r.httpRequest(method, uri, body) if err != nil { return 0, "", err } glog.V(DEBUG_LEVEL).Infof("[api] result: status: %d, content: %s\n", status, content) if status >= 200 && status <= 299 { if result != nil { if err := r.decodeRequest(strings.NewReader(content), result); err != nil { glog.V(DEBUG_LEVEL).Infof("failed to unmarshall the response from marathon, error: %s", err) return status, content, ErrInvalidResponse } } return status, content, nil } switch status { case 500: return status, "", ErrInvalidResponse case 404: return status, "", ErrDoesNotExist } // step: lets decode into a error message var message struct { Message string `json:"message"` } if err := r.decodeRequest(strings.NewReader(content), &message); err != nil { return status, content, ErrInvalidResponse } errorMessage := "unknown error" if message.Message != "" { errorMessage = message.Message } return status, "", fmt.Errorf("%s", errorMessage) }
func main() { flag.Parse() if Options.method_file == "" { glog.Errorf("You have not specified the methods file to import") os.Exit(1) } // step: open and read in the methods yaml contents, err := ioutil.ReadFile(Options.method_file) if err != nil { glog.Errorf("Failed to open|read the methods file: %s, error: %s", Options.method_file, err) os.Exit(1) } // step: unmarshal the yaml var methods []*RestMethod err = yaml.Unmarshal([]byte(contents), &methods) if err != nil { glog.Errorf("Failed to unmarshall the method yaml file: %s, error: %s", Options.method_file, err) os.Exit(1) } // step: construct a hash from the methods uris := make(map[string]*string, 0) for _, method := range methods { uris[fmt.Sprintf("%s:%s", method.Method, method.URI)] = &method.Content } http.HandleFunc("/", func(writer http.ResponseWriter, reader *http.Request) { key := fmt.Sprintf("%s:%s", reader.Method, reader.RequestURI) glog.V(4).Infof("Request: uri: %s, method: %s", reader.RequestURI, reader.Method) if content, found := uris[key]; found { glog.V(4).Infof("Content: %s", *content) writer.Header().Add("Content-Type", "application/json") writer.Write([]byte(*content)) } else if !found { http.Error(writer, "not found", 404) } }) glog.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", Options.port), nil)) }
// UpdateApplication updates an application in Marathon // application: the structure holding the application configuration func (r *marathonClient) UpdateApplication(application *Application) (*DeploymentID, error) { result := new(DeploymentID) glog.V(DEBUG_LEVEL).Infof("updating application: %s", application) uri := fmt.Sprintf("%s/%s", MARATHON_API_APPS, trimRootPath(application.ID)) if err := r.apiPut(uri, &application, result); err != nil { return nil, err } return result, nil }
// ScaleApplicationInstances changes the number of instance an application is running // name: the id used to identify the application // instances: the number of instances you wish to change to // force: used to force the scale operation in case of blocked deployment func (r *marathonClient) ScaleApplicationInstances(name string, instances int, force bool) (*DeploymentID, error) { glog.V(DEBUG_LEVEL).Infof("scaling the application: %s, instance: %d", name, instances) changes := new(Application) changes.ID = validateID(name) changes.Instances = instances uri := fmt.Sprintf("%s/%s?force=%t", MARATHON_API_APPS, trimRootPath(name), force) deployID := new(DeploymentID) if err := r.apiPut(uri, &changes, deployID); err != nil { return nil, err } return deployID, nil }
// RestartApplication performs a rolling restart of marathon application // name: the id used to identify the application func (r *marathonClient) RestartApplication(name string, force bool) (*DeploymentID, error) { glog.V(DEBUG_LEVEL).Infof("restarting the application: %s, force: %s", name, force) deployment := new(DeploymentID) var options struct { Force bool `json:"force"` } options.Force = force if err := r.apiPost(fmt.Sprintf("%s/%s/restart", MARATHON_API_APPS, trimRootPath(name)), &options, deployment); err != nil { return nil, err } return deployment, nil }
// AddEventsListener adds your self as a listener to events from Marathon // channel: a EventsChannel used to receive event on func (r *marathonClient) AddEventsListener(channel EventsChannel, filter int) error { r.Lock() defer r.Unlock() // step: someone has asked to start listening to event, we need to register for events // if we haven't done so already if err := r.RegisterSubscription(); err != nil { return err } if _, found := r.listeners[channel]; !found { glog.V(DEBUG_LEVEL).Infof("adding a watch for events: %d, channel: %v", filter, channel) r.listeners[channel] = filter } return nil }
func (r *marathonClient) httpRequest(method, uri, body string) (int, string, *http.Response, error) { var content string // step: get a member from the cluster marathon, err := r.cluster.GetMember() if err != nil { return 0, "", nil, err } url := fmt.Sprintf("%s/%s", marathon, uri) glog.V(DEBUG_LEVEL).Infof("[http] request: %s, uri: %s, url: %s", method, uri, url) // step: make the http request to marathon request, err := http.NewRequest(method, url, strings.NewReader(body)) if err != nil { return 0, "", nil, err } // step: add any basic auth and the content headers if r.config.HttpBasicAuthUser != "" { request.SetBasicAuth(r.config.HttpBasicAuthUser, r.config.HttpBasicPassword) } request.Header.Add("Content-Type", "application/json") request.Header.Add("Accept", "application/json") response, err := r.httpClient.Do(request) if err != nil { r.cluster.MarkDown() // step: retry the request with another endpoint return r.httpRequest(method, uri, body) } if response.ContentLength != 0 { responseContent, err := ioutil.ReadAll(response.Body) if err != nil { return response.StatusCode, "", response, err } content = string(responseContent) } return response.StatusCode, content, response, nil }