-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Sample application for sending a test report
- Loading branch information
Showing
3 changed files
with
221 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
package main | ||
|
||
// | ||
// Installation instruction (golang tools are required): | ||
// $ make setup | ||
// $ make deps | ||
// $ make build | ||
// | ||
// Sample command: | ||
// $ bin/ubbagent -logtostderr \ | ||
// -reporting-secret fake_reporting_secret.yaml \ | ||
// -service-name your-solution.your-service-id.appspot.com | ||
// -metric-name your-metric-name \ | ||
// -metric-int-value 1 | ||
// | ||
// The service name and metric name are configured when billing | ||
// is setup for your project. | ||
// | ||
// If you deploy Cloud Bees Core Billable solution from Marketplace | ||
// and obtain its reporting secret (look for *-reporting-secret Secret | ||
// in the target namespace), you can use the following: | ||
// $ bin/ubbagent -logtostderr \ | ||
// -reporting-secret reporting_secret.yaml \ | ||
// -service-name cloudbees-core-billable.mp-cloudbees.appspot.com \ | ||
// -metric-name user \ | ||
// -metric-int-value 1 | ||
// | ||
|
||
import ( | ||
"context" | ||
"encoding/base64" | ||
"errors" | ||
"flag" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"time" | ||
|
||
"github.com/GoogleCloudPlatform/ubbagent/config" | ||
"golang.org/x/oauth2/google" | ||
"google.golang.org/api/servicecontrol/v1" | ||
|
||
"github.com/ghodss/yaml" | ||
"github.com/golang/glog" | ||
"github.com/google/uuid" | ||
) | ||
|
||
var secretPath = flag.String("reporting-secret", "", "K8s reporting secret YAML") | ||
var serviceName = flag.String("service-name", "", "Service name") | ||
var metricName = flag.String("metric-name", "", "Metric name") | ||
var metricValue = flag.Int64("metric-int-value", 1, "Metric int64 value") | ||
|
||
type secret struct { | ||
Data secretData `json:"data"` | ||
} | ||
|
||
type secretData struct { | ||
ConsumerID consumerID `json:"consumer-id"` | ||
EntitlementID entitlementID `json:"entitlement-id"` | ||
ReportingKey reportingKey `json:"reporting-key"` | ||
} | ||
|
||
type consumerID string | ||
|
||
func (c *consumerID) UnmarshalJSON(data []byte) error { | ||
if c == nil { | ||
return errors.New("EncodedServiceAccountKey.UnmarshalJSON: nil pointer") | ||
} | ||
decoded, err := decodeBase64Encoded(data) | ||
if err != nil { | ||
return err | ||
} | ||
*c = consumerID(decoded) | ||
return nil | ||
} | ||
|
||
type entitlementID string | ||
|
||
func (c *entitlementID) UnmarshalJSON(data []byte) error { | ||
if c == nil { | ||
return errors.New("EncodedServiceAccountKey.UnmarshalJSON: nil pointer") | ||
} | ||
decoded, err := decodeBase64Encoded(data) | ||
if err != nil { | ||
return err | ||
} | ||
*c = entitlementID(decoded) | ||
return nil | ||
} | ||
|
||
type reportingKey config.EncodedServiceAccountKey | ||
|
||
func (c *reportingKey) UnmarshalJSON(data []byte) error { | ||
if c == nil { | ||
return errors.New("EncodedServiceAccountKey.UnmarshalJSON: nil pointer") | ||
} | ||
decoded, err := decodeBase64Encoded(data) | ||
if err != nil { | ||
return err | ||
} | ||
if decoded == nil { | ||
return err | ||
} | ||
accountKey := config.EncodedServiceAccountKey{} | ||
accountKey.UnmarshalJSON(decoded) | ||
*c = reportingKey(accountKey) | ||
return nil | ||
} | ||
|
||
func decodeBase64Encoded(data []byte) ([]byte, error) { | ||
var decoded []byte | ||
var encodedStr string | ||
|
||
// First we decode the data into a string to get rid of any start and end quotes. | ||
err := yaml.Unmarshal(data, &encodedStr) | ||
if err != nil { | ||
return nil, errors.New("EncodedServiceAccountKey.UnmarshalJSON: not a string value") | ||
} | ||
|
||
decoded, err = base64.StdEncoding.DecodeString(encodedStr) | ||
if err != nil { | ||
return nil, errors.New("EncodedServiceAccountKey.UnmarshalJSON: not a valid base64 value") | ||
} | ||
|
||
return decoded, nil | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
|
||
if *secretPath == "" || *serviceName == "" || *metricName == "" { | ||
fmt.Fprintln(os.Stderr, "Required flags must be specified") | ||
flag.Usage() | ||
os.Exit(2) | ||
} | ||
reportingSecret, err := load(*secretPath) | ||
check(err) | ||
glog.Infof("ReportingSecret=%+v", reportingSecret) | ||
service, err := newServiceControl(reportingSecret.Data.ReportingKey) | ||
check(err) | ||
|
||
opID, err := uuid.NewRandom() | ||
check(err) | ||
op := &servicecontrol.Operation{ | ||
OperationId: opID.String(), | ||
// ServiceControl requires this field but doesn't indicate what it's supposed to be. | ||
OperationName: fmt.Sprintf("%v/report", *serviceName), | ||
StartTime: time.Now().Format(time.RFC3339Nano), | ||
EndTime: time.Now().Format(time.RFC3339Nano), | ||
ConsumerId: string(reportingSecret.Data.ConsumerID), | ||
MetricValueSets: []*servicecontrol.MetricValueSet{ | ||
{ | ||
MetricName: fmt.Sprintf("%v/%v", *serviceName, *metricName), | ||
MetricValues: []*servicecontrol.MetricValue{ | ||
&servicecontrol.MetricValue{ | ||
StartTime: time.Now().Format(time.RFC3339Nano), | ||
EndTime: time.Now().Format(time.RFC3339Nano), | ||
Int64Value: &[]int64{*metricValue}[0], | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
req := &servicecontrol.ReportRequest{ | ||
Operations: []*servicecontrol.Operation{op}, | ||
} | ||
response, err := service.Services.Report(*serviceName, req).Do() | ||
glog.Infof("Response=%v\nError=%v", response, err) | ||
if err == nil { | ||
glog.Infof("SUCCESS!!!") | ||
} else { | ||
glog.Fatalf("DID NOT SUCCEED") | ||
} | ||
} | ||
|
||
func check(err error) { | ||
if err != nil { | ||
glog.Fatalf("Error: %v", err) | ||
} | ||
} | ||
|
||
func newServiceControl(jsonKey []byte) (*servicecontrol.Service, error) { | ||
config, err := google.JWTConfigFromJSON(jsonKey, servicecontrol.ServicecontrolScope) | ||
if err != nil { | ||
return nil, err | ||
} | ||
client := config.Client(context.Background()) | ||
client.Timeout = 30 * time.Second | ||
service, err := servicecontrol.New(client) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return service, nil | ||
} | ||
|
||
func load(path string) (*secret, error) { | ||
data, err := ioutil.ReadFile(path) | ||
if err != nil { | ||
return nil, err | ||
} | ||
secret, err := parse(data) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return secret, nil | ||
} | ||
|
||
func parse(data []byte) (*secret, error) { | ||
c := &secret{} | ||
if err := yaml.Unmarshal(data, c); err != nil { | ||
return nil, err | ||
} | ||
return c, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
apiVersion: v1 | ||
data: | ||
consumer-id: cHJvamVjdDpwci14eHh4LWZha2UteHh4eA== | ||
entitlement-id: ZmZmZmZmZmYtZmZmZi1mZmZmLWZmZmYtZmZmZmZmZmZmZmZm | ||
reporting-key: ZXdvZ0lDSjBlWEJsSWpvZ0luTmxjblpwWTJWZllXTmpiM1Z1ZENJc0NpQWdJbkJ5YjJwbFkzUmZhV1FpT2lBaVkyeHZkV1F0YldGeWEyVjBjR3hoWTJVdGRHOXZiSE1pTEFvZ0lDSndjbWwyWVhSbFgydGxlVjlwWkNJNklDSm1OR1ppTUdRMk16TmhaRFEzWWpFd1pUSmhORFJqTTJaak1HWmlZakEzTlRrNE56Z3lZMkpqSWl3S0lDQWljSEpwZG1GMFpWOXJaWGtpT2lBaUxTMHRMUzFDUlVkSlRpQlFVa2xXUVZSRklFdEZXUzB0TFMwdFhHNU5TVWxGZGtGSlFrRkVRVTVDWjJ0eGFHdHBSemwzTUVKQlVVVkdRVUZUUTBKTFdYZG5aMU5wUVdkRlFVRnZTVUpCVVVONk1HOWlWbXh0VmpBNE1GVm9YRzV1WTNoMGQyZHlUVTFKTm1kMEsyTmhObFJxZGs0dmJrNW5hREZ1ZGsxVU1YUjBObnB0WWpaQ01HSTBTazF2V0V4SE4ycGpMM0Y1T1dKTGEwdERPRkEyWEc1bFdESmpXbkExWkVoT1VsQkZVRXhVWXpGV1RuQldZM0JQTm1ReFVYUnNWbTlOWVdVelZtTXlNbEYzTXpCQ2FsTkhTMU12V0U5V2IyUnNUeXRRTHpZM1hHNXhNMUZhVDNwWmJqRTRNakEyT0ZkM0wwVmxUM0UzWW1GdlQzQmllVlJEUlZRMlJUY3JPRzhyYVUxRVQxSklaVzVyTVRSck1rWjZSMjR6SzJOcU9VWklYRzVUTkhGQ0wxZFNLelF4Wm01bE5XOTFOVTVKU1hFMGFXVllWV2RsTnpaNk9GWXhaRE4xZFdGNWNGaGhZME50VEN0TVpGVnlZWFkxYjBSSU4zbEVTVUo2WEc1TWFHRkdTMjlsU0dKNlJGcFRkR2RTYVVKcFZqWkZielpoVFd4dU4wTndUR3hxYlZOUGRHb3JiVk5TYkhZemRFVTJibFU1TjFacU9FcHRhV05DUTNwV1hHNUpkRGx1TkhSelNFRm5UVUpCUVVWRFoyZEZRVUpTYm5Gd05rbHdlRVpKVW5WMFNUbEJUMWxhWlZaRldDOVhUMHRNYkZSQ2RpdFBUR1JvTWtRdmJEWk9YRzVaU1VwemRXVnhXbVJ6VWxVNVducFdlbE5sTDBGaFNIZHJOQzlEZGtsblRqWm5kakZSTTBWVU1FRlRPVXg1UW1Ka1l6QnlPVk53ZG1KdFZHRkJUWEJ6WEc1b2ExaFhaMlpQVUV4R1MyMDVWRGhMTTFSaWRYZFpXbFJUWW1sR2JsWkhUa2R3VkZOMmJqVXJjMFV5Y1dOU1dEVk1aV0Z1YkVaeE0wTkNTbXhxYTFOc1hHNWFZMDhyVW5ORVVqUlFOREJGVVdVd2FXUXhhek5UWVc5Sk1IUkJTMHRhTkdOQ2JHUmhTMUpCVkZSWE5IQnpOa2R2U0c5eU9WSTJOREJZWTFaUlN6bFdYRzVsVGxKUGFtTXdUbWx0Um5sS1VXWlVTMk5YYVV4SU1WaGlRVFJVUTI5R1YwaE9kVU5sTDJoV1VWUlNWbkpTT1hWb01YVlVTbkI1VUc0MFFrTXpabnBTWEc1TmRIRnlOWGxoVEc1eWJUTXlZMFFyYjBZclZuWllOMUpsVm10TVpuWk1TazVEVlVSVE9FRjFjVkZMUW1kUlJEQTROMlJIVUZKaFRXSkdPVVY1Ym1WcVhHNDVTM1JxU2xOQmNsaHpZbUp3YmxKUVJ6bFNhMEZVU1RsR2FYQnRURnBhY3pSVGFEYzVPV2RZTlZkUE5YUlZObGRCWVZWcVRVUjFNM3B6VEdaQ05DdFBYRzVOTkU5TVFtbDBMM2t3VnpGQlpsTTBUVVkyVG1wbWNFbGFTVWxzT1drMVowSkxVbkJGUVhCMGRtVnRTVzlHWkZsUlkweFRiMkYwU0ZKbGNIQmxOMmxpWEc1aU1IRm5ZaTlVVm5ORllYaGhaMUIyS3poSU4zcEVjVVpYVVV0Q1oxRkROemR6TlVnMFVqTjFXRFJVUzNKUk9XVTBTVkZOTm05cVVFSXhRazlFV0cxalhHNXJlVEpDVUc1TGNsTkJiMWN5Y2xsWlJYcG5OWHBGUW1weWVuZHNPVTFuSzBsaFFYVnViUzl5ZEVORlowbzJTMlpLVlhkWVEzaFdjVFpqVjIxeVZIaENYRzVoUTJaSFRFb3phaTg1YWxoTlEydEhUMnRVWVVoVWRqbGxjSFV3YkM5MFp6TTVOa3hDU1V4TWFXaG5PVzk2VVZvemJIbHJhemRIT1hoWWNtRkViVzVuWEc1S1RFUTVhM05rTTFoM1MwSm5SV054TDBObVJFaGxZMFZ2V2xaaFFXWkxNelZ2WlhsNFMzSXhTMWNyZERaQlRVbEJaV2huVmtwc1l6bEZjV3h0YUhabFhHNVBlVmg0WkRJMVVqZHRlVXBYWlVSS1lqVktUM1JFYzA5TWNEUm5SWHA0YzJsU05TdFdNbk5WZDNoaU5VUTBTRzlST0VJMk4wdHVWakJrTlROeVZHVnRYRzVuWVVsdmVpczNZMmsxY0habk5VVllOR2xRVTFwMlNWcFNVMk5MYkVST1RUTllSRXAwYldaVWFFZGhUbVE0Um1zNGVFcFhXSFphV2tGdlIwRlpVRTA0WEc1U1dXRlVNakZKTldab2EzcFZZakl2VVdFMlNXSXdNRlpzTXpaTFJ6QlZkRGt6ZGxGNWFESTJNM0pzY25OU1dGSk1jbVkxUXpkUWREaHhURW96YjNWWlhHNVRRbE5EU201V2FHeFhXRGxHWkRZeU1YcG9ibXA1VlZWVGRqQmFiR0ZDTW5wR2VHVkJOalJOU0dzNFFrTjFOWEZqU2pobFVVcEVUVE5MYUM5RVUxaFJYRzVrTmxWWVIwbDFaMGc0VVdVM05qRjJNalZOTlRSWE1rMURRWFpvWjB4c1RTdFVUMDFhVkRoRFoxbENjalY1UjFwaU1sQkdaaXRLYldabVRVMUVORTU2WEc1bGJFcDFTMUpsWXpSaWVscHNORkJHUTFjM09IcEpNaTlEWlRaak9IUm5XR04wT0VwdFRVTTJSMmt4YUVSQ01TOHZabFpJU2pjeFJubFNZakJJWVhWUFhHNVRXSFoyVlhRMU9HRXZMMUJzYzFaVWJHTnVjR0oyVUVWUlJUWTBTalo2VWpsbkswUkxOV2hFT0dvNFVVeFZhRmhrZEhGWVlqVmhVWEE0UXpsek5HNVBYRzVpY1VSUVozVm9MMHRKYVZBMVdTdGhVRTlsYUc1M1BUMWNiaTB0TFMwdFJVNUVJRkJTU1ZaQlZFVWdTMFZaTFMwdExTMWNiaUlzQ2lBZ0ltTnNhV1Z1ZEY5bGJXRnBiQ0k2SUNKNGVIZ3RabUZyWlMxeVpYQnZjblJsY2kxNGVIaEFZMnh2ZFdRdGJXRnlhMlYwY0d4aFkyVXRkRzl2YkhNdWFXRnRMbWR6WlhKMmFXTmxZV05qYjNWdWRDNWpiMjBpTEFvZ0lDSmpiR2xsYm5SZmFXUWlPaUFpTVRBMU9EWXlNRE0zT0RRMU1qazVOekkzT0RJeklpd0tJQ0FpWVhWMGFGOTFjbWtpT2lBaWFIUjBjSE02THk5aFkyTnZkVzUwY3k1bmIyOW5iR1V1WTI5dEwyOHZiMkYxZEdneUwyRjFkR2dpTEFvZ0lDSjBiMnRsYmw5MWNta2lPaUFpYUhSMGNITTZMeTl2WVhWMGFESXVaMjl2WjJ4bFlYQnBjeTVqYjIwdmRHOXJaVzRpTEFvZ0lDSmhkWFJvWDNCeWIzWnBaR1Z5WDNnMU1EbGZZMlZ5ZEY5MWNtd2lPaUFpYUhSMGNITTZMeTkzZDNjdVoyOXZaMnhsWVhCcGN5NWpiMjB2YjJGMWRHZ3lMM1l4TDJObGNuUnpJaXdLSUNBaVkyeHBaVzUwWDNnMU1EbGZZMlZ5ZEY5MWNtd2lPaUFpYUhSMGNITTZMeTkzZDNjdVoyOXZaMnhsWVhCcGN5NWpiMjB2Y205aWIzUXZkakV2YldWMFlXUmhkR0V2ZURVd09TOTRlSGd0Wm1GclpTMXlaWEJ2Y25SbGNpMTRlSGdsTkRCamJHOTFaQzF0WVhKclpYUndiR0ZqWlMxMGIyOXNjeTVwWVcwdVozTmxjblpwWTJWaFkyTnZkVzUwTG1OdmJTSUtmUT09 | ||
kind: Secret | ||
type: Opaque |