...

Source file src/github.com/emissary-ingress/emissary/v3/pkg/metriton/protocol.go

Documentation: github.com/emissary-ingress/emissary/v3/pkg/metriton

     1  package metriton
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"io/ioutil"
     8  	"net/http"
     9  )
    10  
    11  // Report is a telemetry report to submit to Metriton.
    12  //
    13  // See: https://github.com/datawire/metriton/blob/master/metriton/scout/jsonschema.py
    14  type Report struct {
    15  	Application string                 `json:"application"` // (required) The name of the application reporting the event
    16  	InstallID   string                 `json:"install_id"`  // (required) Application installation ID (usually a UUID, but technically an opaque string)
    17  	Version     string                 `json:"version"`     // (required) Application version number
    18  	Metadata    map[string]interface{} `json:"metadata"`    // (optional) Additional metadata about the application
    19  }
    20  
    21  // Send the report to the given Metriton endpoint using the given
    22  // httpClient.
    23  //
    24  // The returned *Response may be nil even if there is no error, if
    25  // Metriton has not yet been configured to know about the Report's
    26  // `.Application`; i.e. a Response is only returned for known
    27  // applications.
    28  func (r Report) Send(ctx context.Context, httpClient *http.Client, endpoint string) (*Response, error) {
    29  	body, err := json.MarshalIndent(r, "", "  ")
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	req, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewReader(body))
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	req.Header.Set("Content-Type", "application/json")
    39  
    40  	resp, err := httpClient.Do(req)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	defer resp.Body.Close()
    45  
    46  	respBytes, err := ioutil.ReadAll(resp.Body)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	if len(respBytes) == 0 {
    52  		// not a recognized .Application
    53  		return nil, nil
    54  	}
    55  
    56  	var parsedResp Response
    57  	if err := json.Unmarshal(respBytes, &parsedResp); err != nil {
    58  		return nil, err
    59  	}
    60  	return &parsedResp, nil
    61  }
    62  
    63  // Response is a response from Metriton, after submitting a Report to
    64  // it.
    65  type Response struct {
    66  	AppInfo
    67  
    68  	// Only set for .Application=="aes"
    69  	HardLimit bool `json:"hard_limit"`
    70  
    71  	// Disable submitting any more telemetry for the remaining
    72  	// lifetime of this process.
    73  	//
    74  	// This way, if we ever make another release that turns out to
    75  	// effectively DDoS Metriton, we can adjust the Metriton
    76  	// server's `api.py:handle_report()` to be able to tell the
    77  	// offending processes to shut up.
    78  	DisableScout bool `json:"disable_scout"`
    79  }
    80  
    81  // AppInfo is the information that Metriton knows about an
    82  // application.
    83  //
    84  // There isn't really an otherwise fixed schema for this; Metriton
    85  // returns whatever it reads from
    86  // f"s3://scout-datawire-io/{report.application}/app.json".  However,
    87  // looking at all of the existing app.json files, they all agree on
    88  // the schema
    89  type AppInfo struct {
    90  	Application   string   `json:"application"`
    91  	LatestVersion string   `json:"latest_version"`
    92  	Notices       []Notice `json:"notices"`
    93  }
    94  
    95  // Notice is a notice that should be displayed to the user.
    96  //
    97  // I have no idea what the schema for Notice is, there are none
    98  // currently, and reverse-engineering it from what diagd.py consumes
    99  // isn't worth the effort at this time.
   100  type Notice interface{}
   101  

View as plain text