change api specs
diff --git a/api.go b/api.go index 98c7e16..c1c7643 100644 --- a/api.go +++ b/api.go
@@ -56,7 +56,7 @@ const ( sqlTimeFormat = "2006-01-02 15:04:05.999 -0700 MST" - iso8601 = "2006-01-02T15:04:05.999Z07:00" + iso8601 = "2 mnn006-01-02T15:04:05.999Z07:00" sqliteTimeFormat = "2006-01-02 15:04:05.999-07:00" changeTimeFormat = "2006-01-02 15:04:05.999" ) @@ -136,7 +136,7 @@ services.API().HandleFunc(a.blobEndpoint, a.apiReturnBlobData).Methods("GET") services.API().HandleFunc(a.configStatusEndpoint, a.apiPutConfigStatus).Methods("PUT") services.API().HandleFunc(a.heartbeatEndpoint, a.apiPutHeartbeat).Methods("PUT") - services.API().HandleFunc(a.registerEndpoint, a.apiPostRegister).Methods("POST") + services.API().HandleFunc(a.registerEndpoint, a.apiPutRegister).Methods("POST") a.apiInitialized = true log.Debug("API endpoints initialized") } @@ -365,68 +365,79 @@ w.Write(b) } -func (a *apiManager) apiPostRegister(w http.ResponseWriter, r *http.Request) { +func (a *apiManager) apiPutRegister(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) uuid := vars["uuid"] - if !isValidUuid(uuid) { - a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing gateway uuid") - return - } - created := r.Header.Get("created") - if created == "" { - a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing created") - return - } - name := r.Header.Get("name") - if name == "" { - a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing name") + + // parse body + + body := r.Body + defer body.Close() + bodyBytes, err := ioutil.ReadAll(body) + if err != nil { + log.Errorf("apiPutRegister error: %v", err) + a.writeError(w, http.StatusInternalServerError, API_ERR_INTERNAL, "Failed to read request body.") return } - pod := r.Header.Get("pod") - podType := r.Header.Get("podtype") - serviceType := r.Header.Get("type") + reqBody := ®isterBody{} + err = json.Unmarshal(bodyBytes, reqBody) + if err != nil { + log.Errorf("apiPutRegister error: %v", err) + a.writeError(w, http.StatusInternalServerError, API_ERR_INTERNAL, "Failed to read request body.") + return + } - trackerResp := a.trackerCl.postRegister(uuid, pod, created, name, podType, serviceType) + isValid, reason := reqBody.validateBody(uuid) + if !isValid { + a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, reason) + return + } + + trackerResp := a.trackerCl.putRegister(uuid, reqBody) switch trackerResp.code { case http.StatusOK: - a.writePostRegisterResp(w, trackerResp) + a.writePutRegisterResp(w, trackerResp) default: - log.Infof("putConfigStatus code: %v Reason: %v", trackerResp.code, trackerResp.errString) - a.writeError(w, trackerResp.code, API_ERR_FROM_TRACKER, trackerResp.errString) + log.Infof("apiPutRegister code: %v Reason: %v", trackerResp.code, trackerResp.body) + a.writeError(w, trackerResp.code, API_ERR_FROM_TRACKER, string(trackerResp.body)) } } func (a *apiManager) apiPutConfigStatus(w http.ResponseWriter, r *http.Request) { - configId := r.URL.Query().Get("configid") - if configId == "" { - a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing configId") + // parse body + + body := r.Body + defer body.Close() + bodyBytes, err := ioutil.ReadAll(body) + if err != nil { + log.Errorf("apiPutConfigStatus error: %v", err) + a.writeError(w, http.StatusInternalServerError, API_ERR_INTERNAL, "Failed to read request body.") return } - uuid := r.Header.Get("uuid") - if !isValidUuid(uuid) { - a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing gateway uuid") + + reqBody := &configStatusBody{} + err = json.Unmarshal(bodyBytes, reqBody) + if err != nil { + log.Errorf("apiPutConfigStatus error: %v", err) + a.writeError(w, http.StatusInternalServerError, API_ERR_INTERNAL, "Failed to read request body.") return } - status := r.Header.Get("status") - if status == "" { - a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing status") + + isValid, reason := reqBody.validateBody() + if !isValid { + a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, reason) return } - created := r.Header.Get("created") - if created == "" { - a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing created") - return - } - trackerResp := a.trackerCl.putConfigStatus(configId, status, uuid, created) + trackerResp := a.trackerCl.putConfigStatus(reqBody) switch trackerResp.code { case http.StatusOK: a.writeConfigStatusResp(w, trackerResp) default: - log.Infof("putConfigStatus code: %v Reason: %v", trackerResp.code, trackerResp.errString) - a.writeError(w, trackerResp.code, API_ERR_FROM_TRACKER, trackerResp.errString) + log.Infof("apiPutConfigStatus code: %v Reason: %v", trackerResp.code, trackerResp.body) + a.writeError(w, trackerResp.code, API_ERR_FROM_TRACKER, string(trackerResp.body)) } } @@ -437,31 +448,46 @@ a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing gateway uuid") return } - updated := r.Header.Get("updated") - if updated == "" { - a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing updated") + reported := r.Header.Get("reportedTime") + if reported == "" { + a.writeError(w, http.StatusBadRequest, API_ERR_INVALID_PARAMETERS, "Bad/Missing reportedTime") return } - trackerResp := a.trackerCl.putHeartbeat(uuid, updated) + trackerResp := a.trackerCl.putHeartbeat(uuid, reported) switch trackerResp.code { case http.StatusOK: a.writePutHeartbeatResp(w, trackerResp) default: - log.Infof("putConfigStatus code: %v Reason: %v", trackerResp.code, trackerResp.errString) - a.writeError(w, trackerResp.code, API_ERR_FROM_TRACKER, trackerResp.errString) + log.Infof("apiPutHeartbeat code: %v Reason: %v", trackerResp.code, trackerResp.body) + a.writeError(w, trackerResp.code, API_ERR_FROM_TRACKER, string(trackerResp.body)) } } -func (a *apiManager) writeConfigStatusResp(w http.ResponseWriter, tr trackerResponse) { +func (a *apiManager) writeConfigStatusResp(w http.ResponseWriter, tr *trackerResponse) { w.Header().Add("Content-type", tr.contentType) + _, err := w.Write(tr.body) + if err != nil { + log.Errorf("failed to write response: %v", err) + a.writeError(w, http.StatusInternalServerError, API_ERR_INTERNAL, err.Error()) + } } -func (a *apiManager) writePostRegisterResp(w http.ResponseWriter, tr trackerResponse) { +func (a *apiManager) writePutRegisterResp(w http.ResponseWriter, tr *trackerResponse) { w.Header().Add("Content-type", tr.contentType) + _, err := w.Write(tr.body) + if err != nil { + log.Errorf("failed to write response: %v", err) + a.writeError(w, http.StatusInternalServerError, API_ERR_INTERNAL, err.Error()) + } } -func (a *apiManager) writePutHeartbeatResp(w http.ResponseWriter, tr trackerResponse) { +func (a *apiManager) writePutHeartbeatResp(w http.ResponseWriter, tr *trackerResponse) { w.Header().Add("Content-type", tr.contentType) + _, err := w.Write(tr.body) + if err != nil { + log.Errorf("failed to write response: %v", err) + a.writeError(w, http.StatusInternalServerError, API_ERR_INTERNAL, err.Error()) + } } // call whenever the list of deployments changes @@ -520,3 +546,56 @@ } return r.MatchString(uuid) } + +type registerBody struct { + Uuid string `json:"uuid"` + Pod string `json:"pod"` + PodType string `json:"podType"` + ReportedTime string `json:"reportedTime"` + Name string `json:"name"` + Type string `json:"type"` +} + +func (body *registerBody) validateBody(uuid string) (bool, string) { + switch { + case uuid != body.Uuid: + return false, "UUID in path mismatch UUID in body" + case !isValidUuid(body.Uuid): + return false, "Bad/Missing gateway UUID" + case body.ReportedTime == "": + return false, "Bad/Missing gateway ReportedTimeService" + } + return true, "" +} + +type configStatusBody struct { + StatusDetails []statusDetailsJson `json:"statusDetails"` + ServiceId string `json:"serviceId"` + ReportedTime string `json:"reportedTime"` +} + +type statusDetailsJson struct { + Status string `json:"status"` + ConfigurationId string `json:"configurationId"` + ErrorCode string `json:"errorCode"` + Message string `json:"message"` +} + +func (body *configStatusBody) validateBody() (bool, string) { + switch { + case !isValidUuid(body.ServiceId): + return false, "Bad/Missing gateway ServiceId" + case body.ReportedTime == "": + return false, "Bad/Missing gateway ReportedTimeService" + } + + for _, s := range body.StatusDetails { + switch { + case s.Status == "": + return false, "Bad/Missing configuration Status" + case s.ConfigurationId == "": + return false, "Bad/Missing configuration ConfigurationId" + } + } + return true, "" +}
diff --git a/clients.go b/clients.go index dab68ce..6e47829 100644 --- a/clients.go +++ b/clients.go
@@ -1,7 +1,8 @@ package apiGatewayConfDeploy import ( - "github.com/30x/apid-core" + "bytes" + "encoding/json" "io/ioutil" "net/http" "net/url" @@ -9,6 +10,7 @@ ) const ( + configBearerToken = "apigeesync_bearer_token" trackerConfigStatusEndpoint = "/serviceconfigstatus" trackerHeartbeatEndpoint = "/serviceheartbeat/{uuid}" trackerRegisterEndpoint = "/serviceregister/{uuid}" @@ -16,37 +18,34 @@ ) type trackerClientInterface interface { - putConfigStatus(configId, status, uuid, created string) trackerResponse - postRegister(uuid, pod, created, name, podType, serviceType string) trackerResponse - putHeartbeat(uuid, updated string) trackerResponse + putConfigStatus(body *configStatusBody) *trackerResponse + putRegister(uuid string, body *registerBody) *trackerResponse + putHeartbeat(uuid, reportedTime string) *trackerResponse } type trackerResponse struct { code int contentType string - errString string + body []byte } type trackerClient struct { trackerBaseUrl string clusterId string httpclient *http.Client - handler *tokenEventHandler } -func (t *trackerClient) putConfigStatus(configId, status, uuid, created string) trackerResponse { - uri, err := url.Parse(t.trackerBaseUrl + strings.Replace(trackerConfigStatusEndpoint, "{uuid}", uuid, 1)) +func (t *trackerClient) putConfigStatus(reqBody *configStatusBody) *trackerResponse { + uri, err := url.Parse(t.trackerBaseUrl + trackerConfigStatusEndpoint) if err != nil { log.Errorf("putConfigStatus failed to parse tracker uri: %v", err) return (internalError(err)) } - req, err := http.NewRequest("PUT", uri.String(), nil) - req.Header.Add("configid", configId) - req.Header.Add("Authorization", t.handler.getBearerToken()) - req.Header.Add("status", status) - req.Header.Add("uuid", uuid) - req.Header.Add("created", created) - req.Header.Add("clusterid", t.clusterId) + + bodyBytes, err := json.Marshal(*reqBody) + + req, err := http.NewRequest("PUT", uri.String(), bytes.NewReader(bodyBytes)) + req.Header.Add("Authorization", getBearerToken()) r, err := t.httpclient.Do(req) if err != nil { @@ -54,45 +53,33 @@ return (internalError(err)) } defer r.Body.Close() - res := trackerResponse{} - switch r.StatusCode { - case http.StatusOK: - res.code = r.StatusCode - res.contentType = r.Header.Get("Content-type") - case http.StatusUnauthorized, http.StatusForbidden: - res.code = http.StatusInternalServerError - res.errString = "apid token rejected by tracker" - return res - default: - if 400 <= res.code && res.code < 500 { - res.code = http.StatusInternalServerError - } else { - res.code = r.StatusCode - } - res.contentType = r.Header.Get("Content-type") - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return (internalError(err)) - } - res.errString = string(body) - } - return res + + return parseTrackerResponse(r) } -func (t *trackerClient) postRegister(uuid, pod, created, name, podType, serviceType string) trackerResponse { +func (t *trackerClient) putRegister(uuid string, reqBody *registerBody) *trackerResponse { uri, err := url.Parse(t.trackerBaseUrl + strings.Replace(trackerRegisterEndpoint, "{uuid}", uuid, 1)) if err != nil { log.Errorf("postRegister failed to parse tracker uri: %v", err) return (internalError(err)) } - req, err := http.NewRequest("PUT", uri.String(), nil) - req.Header.Add("Authorization", t.handler.getBearerToken()) - req.Header.Add("pod", pod) - req.Header.Add("podtype", podType) - req.Header.Add("created", created) - req.Header.Add("name", name) - req.Header.Add("type", serviceType) - req.Header.Add("clusterid", t.clusterId) + + body := serviceRegisterBody{ + ClusterId: t.clusterId, + Pod: reqBody.Pod, + PodType: reqBody.Type, + ReportedTime: reqBody.ReportedTime, + Name: reqBody.Name, + Type: reqBody.Type, + } + + bodyBytes, err := json.Marshal(body) + if err != nil { + return (internalError(err)) + } + + req, err := http.NewRequest("PUT", uri.String(), bytes.NewReader(bodyBytes)) + req.Header.Add("Authorization", getBearerToken()) r, err := t.httpclient.Do(req) if err != nil { @@ -100,41 +87,18 @@ return (internalError(err)) } defer r.Body.Close() - res := trackerResponse{} - switch r.StatusCode { - case http.StatusOK: - res.code = r.StatusCode - res.contentType = r.Header.Get("Content-type") - case http.StatusUnauthorized, http.StatusForbidden: - res.code = http.StatusInternalServerError - res.errString = "apid token rejected by tracker" - return res - default: - if 400 <= res.code && res.code < 500 { - res.code = http.StatusInternalServerError - } else { - res.code = r.StatusCode - } - res.contentType = r.Header.Get("Content-type") - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return (internalError(err)) - } - res.errString = string(body) - } - return res + return parseTrackerResponse(r) } -func (t *trackerClient) putHeartbeat(uuid, updated string) trackerResponse { +func (t *trackerClient) putHeartbeat(uuid, reported string) *trackerResponse { uri, err := url.Parse(t.trackerBaseUrl + strings.Replace(trackerHeartbeatEndpoint, "{uuid}", uuid, 1)) if err != nil { log.Errorf("putHeartbeat failed to parse tracker uri: %v", err) return internalError(err) } req, err := http.NewRequest("PUT", uri.String(), nil) - req.Header.Add("Authorization", t.handler.getBearerToken()) - req.Header.Add("updated", updated) - req.Header.Add("clusterid", t.clusterId) + req.Header.Add("Authorization", getBearerToken()) + req.Header.Add("reportedTime", reported) r, err := t.httpclient.Do(req) if err != nil { @@ -142,52 +106,54 @@ return internalError(err) } defer r.Body.Close() - res := trackerResponse{} + return parseTrackerResponse(r) +} + +func internalError(err error) *trackerResponse { + res := &trackerResponse{} + res.code = http.StatusInternalServerError + res.body = []byte(err.Error()) + return res +} + +func getBearerToken() string { + return "Bearer " + config.GetString(configBearerToken) +} + +func parseTrackerResponse(r *http.Response) *trackerResponse { + trackerBody, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Errorf("apid failed to read response body: %v", err) + return (internalError(err)) + } + res := &trackerResponse{} switch r.StatusCode { case http.StatusOK: res.code = r.StatusCode + res.body = trackerBody res.contentType = r.Header.Get("Content-type") case http.StatusUnauthorized, http.StatusForbidden: res.code = http.StatusInternalServerError - res.errString = "apid token rejected by tracker" - return res + res.body = []byte("apid token rejected by tracker") + log.Errorf("%v: %v, %v", res.body, r.StatusCode, trackerBody) + case http.StatusNotFound: + res.code = http.StatusInternalServerError + res.body = []byte("apid cannot connect to tracker") + log.Errorf("%v: %v, %v", res.body, r.StatusCode, trackerBody) default: - if 400 <= res.code && res.code < 500 { - res.code = http.StatusInternalServerError - } else { - res.code = r.StatusCode - } + log.Infof("Abnormal Response from Tracker: %v, %v", r.StatusCode, trackerBody) + res.code = r.StatusCode + res.body = trackerBody res.contentType = r.Header.Get("Content-type") - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return internalError(err) - } - res.errString = string(body) } return res } -func internalError(err error) (res trackerResponse) { - res.code = http.StatusInternalServerError - res.errString = err.Error() - return res -} - -type tokenEventHandler struct { - token string -} - -func (h *tokenEventHandler) Handle(e apid.Event) { - if token, ok := e.(string); ok { - h.token = token - } -} - -func (h *tokenEventHandler) getBearerToken() string { - return "Bearer " + h.token -} - -func (h *tokenEventHandler) initListener(services apid.Services) { - - services.Events().Listen(ApigeeSyncTokenSelector, h) +type serviceRegisterBody struct { + ClusterId string `json:"clusterId"` + Pod string `json:"pod"` + PodType string `json:"podType"` + ReportedTime string `json:"reportedTime"` + Name string `json:"name"` + Type string `json:"type"` }
diff --git a/init.go b/init.go index b1d2b7c..4f4bd80 100644 --- a/init.go +++ b/init.go
@@ -129,11 +129,6 @@ } apidClusterId = config.GetString(configApidClusterID) - // initialize token handler - - tokenHandler := &tokenEventHandler{} - tokenHandler.initListener(services) - // initialize tracker client client := &trackerClient{ @@ -145,11 +140,10 @@ }, Timeout: httpTimeout, CheckRedirect: func(req *http.Request, _ []*http.Request) error { - req.Header.Set("Authorization", tokenHandler.getBearerToken()) + req.Header.Set("Authorization", getBearerToken()) return nil }, }, - handler: tokenHandler, } // initialize db manager