diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 4c2ebaf..99e0124 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,9 +1,7 @@ { - "ImportPath": "github.com/ahmetalpbalkan/azure-extensions-cli", + "ImportPath": "github.com/Azure/azure-extensions-cli", "GoVersion": "go1.5", - "Packages": [ - "./..." - ], + "GodepVersion": "v74", "Deps": [ { "ImportPath": "github.com/Azure/azure-sdk-for-go/Godeps/_workspace/src/golang.org/x/crypto/pkcs12", @@ -35,6 +33,18 @@ "Comment": "v0.8.7-55-gf7f79f7", "Rev": "f7f79f729e0fbe2fcc061db48a9ba0263f588252" }, + { + "ImportPath": "github.com/approvals/go-approval-tests", + "Rev": "6f75c0a8ecdf051e948b74b02f7929a4fd8915ca" + }, + { + "ImportPath": "github.com/approvals/go-approval-tests/reporters", + "Rev": "6f75c0a8ecdf051e948b74b02f7929a4fd8915ca" + }, + { + "ImportPath": "github.com/approvals/go-approval-tests/utils", + "Rev": "6f75c0a8ecdf051e948b74b02f7929a4fd8915ca" + }, { "ImportPath": "github.com/codegangsta/cli", "Comment": "1.2.0-193-gf9cc300", diff --git a/README.md b/README.md index 1063cff..0690329 100644 --- a/README.md +++ b/README.md @@ -55,24 +55,22 @@ USAGE: azure-extensions-cli [global options] command [command options] [arguments...] COMMANDS: - new-extension-manifest Creates an XML file used to publish or update extension. - new-extension Creates a new type of extension, not for releasing new versions. - new-extension-version Publishes a new type of extension internally. - promote-single-region Promote published internal extension to a PROD Location. - promote-two-regions Promote published extension to two PROD Locations. - promote-to-prod Promote published extension to all PROD Locations. - list-versions Lists all published extension versions for subscription + new-extension-manifest Creates an XML file used to publish or update extension. + new-extension Creates a new type of extension, not for releasing new versions. + new-extension-version Publishes a new type of extension internally. + promote Promote published internal extension to one or more PROD Locations. + promote-all-regions Promote published extension to all PROD Locations. + list-versions Lists all published extension versions for subscription replication-status Retrieves replication status for an uploaded extension package unpublish-version Marks the specified version of the extension internal. Does not delete. - delete-version Deletes the extension version. It should be unpublished first. - help, h Shows a list of commands or help for one command + delete-version Deletes the extension version. It should be unpublished first. + help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help --version, -v print the version ``` - ## Installing (or building from source) You can head over to the **Releases** section to download a binary built for various platforms. @@ -81,10 +79,36 @@ If you need to compile from the source code, make sure you have Go compiler 1.6+ Check out the project, set the GOPATH environment variable correctly (if necessary) and run `go build`. This should compile a binary. -## Author +## Overview + +The CLI makes it easy (easier) to publish an Azure extension. An example workflow is provided below. This workflow +assumes an extension type already exists, which is why the command **new-extension-version** is used. (If the type does +not exist use substitute for the command new-extension instead.) + +Not all command line parameters are shown for each command, only the salient options are shown. + +Step 1 - create an extension manifest. + + 1. ./azure-extensions-cli new-extension-manifest + +Step 2 - publish an extension internally. + + 1. ./azure-extensions-cli new-extension-version + +Step 3 - rollout the extension to Azure, by slowly including more and more regions. It is recommended that you pause +24 hours between regions. -Ahmet Alp Balkan +> Every time a new region is added, the previous regions must be included with the promote command. + + 1. ./azure-extensions-cli promote --region "West Central US" + 1. ./azure-extensions-cli promote --region "West Central US" --region "North US" + 1. ./azure-extensions-cli promote --region "West Central US" --region "North US" --region "West US" + 1. ./azure-extensions-cli promote ... + +Step 4 - promote the extension to all Azure regions. + 1. ./azure-extensions-cli promote-all-regions + ## TODO - [ ] make `replication-status` exit with appropriate code if replication is not completed. diff --git a/main.go b/main.go index 2766929..5111565 100644 --- a/main.go +++ b/main.go @@ -61,14 +61,10 @@ var ( flStorageAccount = cli.StringFlag{ Name: "storage-account", Usage: "Name of an existing storage account to be used in uploading the extension package temporarily."} - flRegion1 = cli.StringFlag{ - Name: "region-1", - Usage: "Primary pilot location to roll out the extension (e.g. 'Japan East')", - EnvVar: "REGION1"} - flRegion2 = cli.StringFlag{ - Name: "region-2", - Usage: "Primary pilot location to roll out the extension (e.g. 'Brazil South')", - EnvVar: "REGION2"} + flRegion = cli.StringSliceFlag{ + Name: "region", + Usage: "List of one or more regions to rollout an extension (e.g. 'Japan East')", + } flJSON = cli.BoolFlag{ Name: "json", Usage: "Print output as JSON"} @@ -120,21 +116,17 @@ func main() { Usage: "Publishes a new type of extension internally.", Flags: []cli.Flag{flMgtURL, flSubsID, flSubsCert, flManifest}, Action: updateExtension}, - {Name: "promote-single-region", - Usage: "Promote published internal extension to PROD in a Location.", - Flags: []cli.Flag{flMgtURL, flSubsID, flSubsCert, flManifest, flRegion1}, - Action: promoteToFirstSlice}, - {Name: "promote-two-regions", - Usage: "Promote published extension to PROD in two Locations.", - Flags: []cli.Flag{flMgtURL, flSubsID, flSubsCert, flManifest, flRegion1, flRegion2}, - Action: promoteToSecondSlice}, + {Name: "promote", + Usage: "Promote published internal extension to PROD in one or more locations.", + Flags: []cli.Flag{flMgtURL, flSubsID, flSubsCert, flManifest, flRegion}, + Action: promoteToRegions}, {Name: "promote-all-regions", Usage: "Promote published extension to all Locations.", Flags: []cli.Flag{flMgtURL, flSubsID, flSubsCert, flManifest}, Action: promoteToAllRegions}, {Name: "list-versions", Usage: "Lists all published extension versions for subscription", - Flags: []cli.Flag{flMgtURL, flSubsID, flSubsCert}, + Flags: []cli.Flag{flMgtURL, flSubsID, flSubsCert, flJSON}, Action: listVersions}, {Name: "replication-status", Usage: "Retrieves replication status for an uploaded extension package", diff --git a/manifest.go b/manifest.go index 68b01a4..e1a9ead 100644 --- a/manifest.go +++ b/manifest.go @@ -1,76 +1,175 @@ package main import ( - "os" - "text/template" + "encoding/xml" + "fmt" log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "io/ioutil" + "strings" ) +type certificate struct { + StoreLocation string `xml:"StoreLocation,omitempty"` + StoreName string `xml:"StoreName,omitempty"` + ThumbprintRequired bool `xml:"ThumbprintRequired,omitempty"` + ThumbprintAlgorithm string `xml:"ThumbprintAlgorithm,omitempty"` +} + +// NOTE(@boumenot): there is probably a better way to express this. If +// you know please share... +// +// The only difference between ExtensionImage and ExtensionImageGlobal is the +// Regions element. This element can be in three different states to my +// knowledge. +// +// 1. not defined +// 2. Region1;Region2 +// 3. +// +// Case (1) occurs when an extension is first published. Case(2) occurs when +// an extension is promoted to one or two regions. Case (3) occurs when an +// extension is published to all regions. +// +// I do not know how to express all three cases using Go's XML serializer. +// +type extensionImage struct { + XMLName string `xml:"ExtensionImage"` + NS string `xml:"xmlns,attr"` + ProviderNameSpace string `xml:"ProviderNameSpace"` + Type string `xml:"Type"` + Version string `xml:"Version"` + Label string `xml:"Label"` + HostingResources string `xml:"HostingResources"` + MediaLink string `xml:"MediaLink"` + Certificate *certificate `xml:"Certificate,omitempty"` + PublicConfigurationSchema string `xml:"PublicConfigurationSchema,omitempty"` + PrivateConfigurationSchema string `xml:"PrivateConfigurationSchema,omitempty"` + Description string `xml:"Description"` + BlockRoleUponFailure string `xml:"BlockRoleUponFailure,omitempty"` + IsInternalExtension bool `xml:"IsInternalExtension"` + Eula string `xml:"Eula,omitempty"` + PrivacyURI string `xml:"PrivacyUri,omitempty"` + HomepageURI string `xml:"HomepageUri,omitempty"` + IsJSONExtension bool `xml:"IsJsonExtension,omitempty"` + CompanyName string `xml:"CompanyName,omitempty"` + SupportedOS string `xml:"SupportedOS,omitempty"` + Regions string `xml:"Regions,omitempty"` +} + +type extensionImageGlobal struct { + XMLName string `xml:"ExtensionImage"` + NS string `xml:"xmlns,attr"` + ProviderNameSpace string `xml:"ProviderNameSpace"` + Type string `xml:"Type"` + Version string `xml:"Version"` + Label string `xml:"Label"` + HostingResources string `xml:"HostingResources"` + MediaLink string `xml:"MediaLink"` + Certificate *certificate `xml:"Certificate,omitempty"` + PublicConfigurationSchema string `xml:"PublicConfigurationSchema,omitempty"` + PrivateConfigurationSchema string `xml:"PrivateConfigurationSchema,omitempty"` + Description string `xml:"Description"` + BlockRoleUponFailure string `xml:"BlockRoleUponFailure,omitempty"` + IsInternalExtension bool `xml:"IsInternalExtension"` + Eula string `xml:"Eula,omitempty"` + PrivacyURI string `xml:"PrivacyUri,omitempty"` + HomepageURI string `xml:"HomepageUri,omitempty"` + IsJSONExtension bool `xml:"IsJsonExtension,omitempty"` + CompanyName string `xml:"CompanyName,omitempty"` + SupportedOS string `xml:"SupportedOS,omitempty"` + Regions string `xml:"Regions"` +} + +type extensionManifest interface { + Marshal() ([]byte, error) +} + +func isGuestAgent(providerNameSpace string) bool { + return "Microsoft.OSTCLinuxAgent" == providerNameSpace +} + +func newExtensionImageManifest(filename string, regions []string) (extensionManifest, error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + var manifest extensionImage + err = xml.Unmarshal(b, &manifest) + if err != nil { + return nil, err + } + + manifest.Regions = strings.Join(regions, ";") + + if !isGuestAgent(manifest.ProviderNameSpace) { + manifest.IsInternalExtension = false + } else { + log.Debug("VM agent namespace detected, IsInternalExtension ignored") + } + + return &manifest, nil +} + +func newExtensionImageGlobalManifest(filename string) (extensionManifest, error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + var manifest extensionImageGlobal + err = xml.Unmarshal(b, &manifest) + if err != nil { + return nil, err + } + + manifest.IsInternalExtension = !isGuestAgent(manifest.ProviderNameSpace) + return &manifest, nil +} + +func (ext *extensionImage) Marshal() ([]byte, error) { + return xml.Marshal(*ext) +} + +func (ext *extensionImageGlobal) Marshal() ([]byte, error) { + return xml.Marshal(*ext) +} + func newExtensionManifest(c *cli.Context) { cl := mkClient(checkFlag(c, flMgtURL.Name), checkFlag(c, flSubsID.Name), checkFlag(c, flSubsCert.Name)) storageRealm := checkFlag(c, flStorageRealm.Name) storageAccount := checkFlag(c, flStorageAccount.Name) extensionPkg := checkFlag(c, flPackage.Name) - var p struct { - Namespace, Name, Version, BlobURL, Label, Description, Eula, Privacy, Homepage, Company, OS string - } - flags := []struct { - ref *string - fl string - }{ - {&p.Namespace, flNamespace.Name}, - {&p.Name, flName.Name}, - {&p.Version, flVersion.Name}, - {&p.Label, "label"}, - {&p.Description, "description"}, - {&p.Eula, "eula-url"}, - {&p.Privacy, "privacy-url"}, - {&p.Homepage, "homepage-url"}, - {&p.Company, "company"}, - {&p.OS, "supported-os"}, - } - for _, f := range flags { - *f.ref = checkFlag(c, f.fl) - } - // Upload extension blob blobURL, err := uploadBlob(cl, storageRealm, storageAccount, extensionPkg) if err != nil { log.Fatal(err) } log.Debugf("Extension package uploaded to: %s", blobURL) - p.BlobURL = blobURL - - // doing a text template is easier and let us create comments (xml encoder can't) - // that are used as placeholders later on. - manifestXML := ` - - - {{.Namespace}} - {{.Name}} - {{.Version}} - - VmRole - {{.BlobURL}} - {{.Description}} - true - {{.Eula}} - {{.Privacy}} - {{.Homepage}} - true - {{.Company}} - {{.OS}} - - -` - tpl, err := template.New("manifest").Parse(manifestXML) - if err != nil { - log.Fatalf("template parse error: %v", err) + + manifest := extensionImage{ + ProviderNameSpace: checkFlag(c, flNamespace.Name), + Type: checkFlag(c, flName.Name), + Version: checkFlag(c, flVersion.Name), + Label: "label", + Description: "description", + IsInternalExtension: true, + MediaLink: blobURL, + Eula: "eula-url", + PrivacyURI: "privacy-url", + HomepageURI: "homepage-url", + IsJSONExtension: true, + CompanyName: "company", + SupportedOS: "supported-os", } - if err = tpl.Execute(os.Stdout, p); err != nil { - log.Fatalf("template execute error: %v", err) + + bs, err := xml.MarshalIndent(manifest, "", " ") + if err != nil { + log.Fatalf("xml marshall error: %v", err) } + + fmt.Println(string(bs)) } diff --git a/manifest_test.TestRoundTripExtensionImage.approved.txt b/manifest_test.TestRoundTripExtensionImage.approved.txt new file mode 100644 index 0000000..a358227 --- /dev/null +++ b/manifest_test.TestRoundTripExtensionImage.approved.txt @@ -0,0 +1,17 @@ + + Microsoft.OSCTExtensions + CustomScriptForLinux + 4.3.2.1 + + VmRole + http://localhost/extension.zip + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux + South Central US + \ No newline at end of file diff --git a/manifest_test.TestRoundTripExtensionImageGlobal.approved.txt b/manifest_test.TestRoundTripExtensionImageGlobal.approved.txt new file mode 100644 index 0000000..906267b --- /dev/null +++ b/manifest_test.TestRoundTripExtensionImageGlobal.approved.txt @@ -0,0 +1,17 @@ + + Microsoft.OSCTExtensions + CustomScriptForLinux + 4.3.2.1 + + VmRole + http://localhost/extension.zip + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux + + \ No newline at end of file diff --git a/manifest_test.TestRoundTripExtensionImageGlobal.received.txt b/manifest_test.TestRoundTripExtensionImageGlobal.received.txt new file mode 100644 index 0000000..afdd40f --- /dev/null +++ b/manifest_test.TestRoundTripExtensionImageGlobal.received.txt @@ -0,0 +1,17 @@ + + Microsoft.OSCTExtensions + CustomScriptForLinux + 4.3.2.1 + + VmRole + http://localhost/extension.zip + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux + + \ No newline at end of file diff --git a/manifest_test.TestRoundTripXmlExtensionImage.approved.txt b/manifest_test.TestRoundTripXmlExtensionImage.approved.txt new file mode 100644 index 0000000..af44b9c --- /dev/null +++ b/manifest_test.TestRoundTripXmlExtensionImage.approved.txt @@ -0,0 +1,26 @@ + + Microsoft.OSCTExtensions + PaaS + 4.3.2.1 + + WebRole|WorkerRole + http://localhost/extension.zip + + LocalMachine + My + true + sha1 + + --public-configuration-schema-- + --private-configuration-schema-- + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + false + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux + South Central US + \ No newline at end of file diff --git a/manifest_test.TestRoundTripXmlExtensionImageGlobal.approved.txt b/manifest_test.TestRoundTripXmlExtensionImageGlobal.approved.txt new file mode 100644 index 0000000..ca03442 --- /dev/null +++ b/manifest_test.TestRoundTripXmlExtensionImageGlobal.approved.txt @@ -0,0 +1,26 @@ + + Microsoft.OSCTExtensions + PaaS + 4.3.2.1 + + WebRole|WorkerRole + http://localhost/extension.zip + + LocalMachine + My + true + sha1 + + --public-configuration-schema-- + --private-configuration-schema-- + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + false + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux + + \ No newline at end of file diff --git a/manifest_test.TestSuppressRegionWhenEmpty.approved.txt b/manifest_test.TestSuppressRegionWhenEmpty.approved.txt new file mode 100644 index 0000000..a43f076 --- /dev/null +++ b/manifest_test.TestSuppressRegionWhenEmpty.approved.txt @@ -0,0 +1,16 @@ + + Microsoft.OSCTExtensions + CustomScriptForLinux + 4.3.2.1 + + VmRole + http://localhost/extension.zip + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux + \ No newline at end of file diff --git a/manifest_test.go b/manifest_test.go new file mode 100644 index 0000000..38ae7fd --- /dev/null +++ b/manifest_test.go @@ -0,0 +1,170 @@ +package main + +import ( + "bytes" + "encoding/xml" + "testing" + + "github.com/approvals/go-approval-tests" +) + +func TestRoundTripExtensionImage(t *testing.T) { + xmlString := []byte(` + Microsoft.OSCTExtensions + CustomScriptForLinux + 4.3.2.1 + + VmRole + http://localhost/extension.zip + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux + South Central US +`) + + var obj extensionImage + err := xml.Unmarshal(xmlString, &obj) + if err != nil { + t.Fatal(err) + } + + bs, err := xml.MarshalIndent(obj, "", " ") + if err != nil { + t.Fatal(err) + } + + err = approvaltests.Verify(t, bytes.NewReader(bs)) + if err != nil { + t.Fatal(err) + } +} + +func TestRoundTripXmlExtensionImage(t *testing.T) { + xmlString := []byte(` + Microsoft.OSCTExtensions + PaaS + 4.3.2.1 + + WebRole|WorkerRole + http://localhost/extension.zip + + LocalMachine + My + true + sha1 + + --public-configuration-schema-- + --private-configuration-schema-- + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + false + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux + South Central US +`) + + var obj extensionImage + err := xml.Unmarshal(xmlString, &obj) + if err != nil { + t.Fatal(err) + } + + bs, err := xml.MarshalIndent(obj, "", " ") + if err != nil { + t.Fatal(err) + } + + err = approvaltests.Verify(t, bytes.NewReader(bs)) + if err != nil { + t.Fatal(err) + } +} + +func TestSuppressRegionWhenEmpty(t *testing.T) { + xmlString := []byte(` + Microsoft.OSCTExtensions + CustomScriptForLinux + 4.3.2.1 + + VmRole + http://localhost/extension.zip + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux +`) + + var obj extensionImage + err := xml.Unmarshal(xmlString, &obj) + if err != nil { + t.Fatal(err) + } + + bs, err := xml.MarshalIndent(obj, "", " ") + if err != nil { + t.Fatal(err) + } + + err = approvaltests.Verify(t, bytes.NewReader(bs)) + if err != nil { + t.Fatal(err) + } +} + +func TestRoundTripXmlExtensionImageGlobal(t *testing.T) { + xmlString := []byte(` + Microsoft.OSCTExtensions + PaaS + 4.3.2.1 + + WebRole|WorkerRole + http://localhost/extension.zip + + LocalMachine + My + true + sha1 + + --public-configuration-schema-- + --private-configuration-schema-- + Please consider using Microsoft.Azure.Extensions.CustomScript instead. + false + true + https://github.com/Azure/azure-linux-extensions/blob/master/LICENSE-2_0.txt + http://www.microsoft.com/privacystatement/en-us/OnlineServices/Default.aspx + https://github.com/Azure/azure-linux-extensions + true + Microsoft + Linux + +`) + + var obj extensionImageGlobal + err := xml.Unmarshal(xmlString, &obj) + if err != nil { + t.Fatal(err) + } + + bs, err := xml.MarshalIndent(obj, "", " ") + if err != nil { + t.Fatal(err) + } + + err = approvaltests.Verify(t, bytes.NewReader(bs)) + if err != nil { + t.Fatal(err) + } +} diff --git a/promote.go b/promote.go index 3722106..9f3dd4d 100644 --- a/promote.go +++ b/promote.go @@ -1,74 +1,53 @@ package main import ( - "bytes" - "fmt" - "io/ioutil" "strings" log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" ) -func promoteToFirstSlice(c *cli.Context) { - if err := promoteExtension(c, mkRegionElement( - checkFlag(c, flRegion1.Name))); err != nil { - log.Fatal(err) +func promoteToRegions(c *cli.Context) { + regions := c.StringSlice(flRegion.Name) + + if len(regions) == 0 { + log.Fatalf("At least one region must be specified!") + return } - log.Info("Extension is promoted to PROD in one region. See replication-status.") -} -func promoteToSecondSlice(c *cli.Context) { - if err := promoteExtension(c, mkRegionElement( - checkFlag(c, flRegion1.Name), - checkFlag(c, flRegion2.Name))); err != nil { + + if err := promoteExtension(c, func() (extensionManifest, error) { + return newExtensionImageManifest(checkFlag(c, flManifest.Name), regions) + }); err != nil { log.Fatal(err) } - log.Info("Extension is promoted to PROD in two regions. See replication-status.") + + log.Infof("Extension is promoted to PROD in %s. See replication-status.", strings.Join(regions, ",")) } func promoteToAllRegions(c *cli.Context) { - regions := `` // replace placeholder with empty string to omit the element. - if err := promoteExtension(c, regions); err != nil { + if err := promoteExtension(c, func() (extensionManifest, error) { + return newExtensionImageGlobalManifest(checkFlag(c, flManifest.Name)) + }); err != nil { log.Fatal(err) } + log.Info("Extension is promoted to all regions. See replication-status.") } -func promoteExtension(c *cli.Context, regionsXML string) error { - b, err := updateManifestRegions(checkFlag(c, flManifest.Name), regionsXML) +func promoteExtension(c *cli.Context, factory func() (extensionManifest, error)) error { + manifest, err := factory() if err != nil { return err } - if err := publishExtension(c, "UpdateExtension", b, - mkClient(checkFlag(c, flMgtURL.Name), checkFlag(c, flSubsID.Name), checkFlag(c, flSubsCert.Name)).UpdateExtension); err != nil { - return err - } - return nil -} - -func mkRegionElement(regions ...string) string { - return fmt.Sprintf(`%s`, strings.Join(regions, ";")) -} -// updateManifestRegions makes an in-memory update to the -// placeholder string in the manifest XML for further usage, and -// sets according to whether the package -// should remain internal (vm agent) or not (extensions) -func updateManifestRegions(manifestPath string, regionsXMLElement string) ([]byte, error) { - b, err := ioutil.ReadFile(manifestPath) + b, err := manifest.Marshal() if err != nil { - return nil, fmt.Errorf("Error reading manifest: %v", err) + return err } - // todo: improve this - b = bytes.Replace(b, []byte(``), []byte(regionsXMLElement), 1) - - updateInternal := !bytes.Contains(b, []byte(`Microsoft.OSTCLinuxAgent`)) - if updateInternal { - b = bytes.Replace(b, []byte(`true`), []byte(`false`), 1) - } else { - log.Debug("VM agent namespace detected, IsInternalExtension ignored") + if err := publishExtension(c, "UpdateExtension", b, + mkClient(checkFlag(c, flMgtURL.Name), checkFlag(c, flSubsID.Name), checkFlag(c, flSubsCert.Name)).UpdateExtension); err != nil { + return err } - - return b, err + return nil } diff --git a/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go b/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go deleted file mode 100644 index 3187f6d..0000000 --- a/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "github.com/Sirupsen/logrus" - "gopkg.in/gemnasium/logrus-airbrake-hook.v2" -) - -var log = logrus.New() - -func init() { - log.Formatter = new(logrus.TextFormatter) // default - log.Hooks.Add(airbrake.NewHook(123, "xyz", "development")) -} - -func main() { - log.WithFields(logrus.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") - - log.WithFields(logrus.Fields{ - "omg": true, - "number": 122, - }).Warn("The group's number increased tremendously!") - - log.WithFields(logrus.Fields{ - "omg": true, - "number": 100, - }).Fatal("The ice breaks!") -} diff --git a/vendor/github.com/approvals/go-approval-tests/.gitignore b/vendor/github.com/approvals/go-approval-tests/.gitignore new file mode 100644 index 0000000..116a346 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/.gitignore @@ -0,0 +1,25 @@ +*.received.* +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/approvals/go-approval-tests/.travis.yml b/vendor/github.com/approvals/go-approval-tests/.travis.yml new file mode 100644 index 0000000..2f2aa09 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/.travis.yml @@ -0,0 +1,16 @@ +sudo: false + +language: go + +go: + - 1.6 + +install: + - go get -u github.com/golang/lint/golint + +script: + - test -z "$(go fmt -s -l -w ./... | tee /dev/stderr)" + - golint -set_exit_status ./... + - go vet ./... + - go build -v ./... + - go test -v ./... \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/LICENSE.md b/vendor/github.com/approvals/go-approval-tests/LICENSE.md new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/vendor/github.com/approvals/go-approval-tests/README.md b/vendor/github.com/approvals/go-approval-tests/README.md new file mode 100644 index 0000000..9531d95 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/README.md @@ -0,0 +1,53 @@ +# ApprovalTests.go + +ApprovalTests for go + +[![Build Status](https://travis-ci.org/approvals/go-approval-tests.png?branch=master)](https://travis-ci.org/approvals/go-approval-tests) + +# Golden master Verification Library +ApprovalTests allows for easy testing of larger objects, strings and anything else that can be saved to a file (images, sounds, csv, etc...) + +#Examples +##In Project +Note: ApprovalTests uses approvaltests to test itself. Therefore there are many examples in the code itself. + + * [approvals_test.go](approvals_test.go) + +##JSON +VerifyJSONBytes - Simple Formatting for easy comparison. Also uses the .json file extension + +```go +func TestVerifyJSON(t *testing.T) { + jsonb := []byte("{ \"foo\": \"bar\", \"age\": 42, \"bark\": \"woof\" }") + VerifyJSONBytes(t, jsonb) +} +``` +Matches file: approvals_test.TestVerifyJSON.received.json + +```json +{ + "age": 42, + "bark": "woof", + "foo": "bar" +} +``` + +##Reporters +ApprovalTests becomes *much* more powerful with reporters. Reporters launch programs on failure to help you understand, fix and approve results. + +You can make your own easily, [here's an example](reporters/beyond_compare.go) +You can also declare which one to use. Either at the +### Method level +```go +r := UseReporter(reporters.NewIntelliJ()) +defer r.Close() +``` +### Test Level +```go +func TestMain(m *testing.M) { + r := UseReporter(reporters.NewBeyondCompareReporter()) + defer r.Close() + + os.Exit(m.Run()) +} +``` diff --git a/vendor/github.com/approvals/go-approval-tests/TODO.md b/vendor/github.com/approvals/go-approval-tests/TODO.md new file mode 100644 index 0000000..bd53e51 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/TODO.md @@ -0,0 +1 @@ +- [ ] Newbie Reporters ? diff --git a/vendor/github.com/approvals/go-approval-tests/approval_name.go b/vendor/github.com/approvals/go-approval-tests/approval_name.go new file mode 100644 index 0000000..1ef043e --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approval_name.go @@ -0,0 +1,130 @@ +package approvaltests + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "runtime" + "strings" +) + +type approvalName struct { + pc uintptr + fullName string + name string + fileName string + fileLine int +} + +func newApprovalName(pc uintptr, f *runtime.Func) (*approvalName, error) { + namer := &approvalName{ + pc: pc, + fullName: f.Name(), + } + + namer.fileName, namer.fileLine = f.FileLine(pc) + + splits := strings.Split(namer.fullName, ".") + namer.name = splits[len(splits)-1] + + return namer, nil +} + +// Walk the call stack, and try to find the test method that was executed. +// The test method is identified by looking for the test runner, which is +// *assumed* to be common across all callers. The test runner has a Name() of +// 'testing.tRunner'. The method immediately previous to this is the test +// method. +func getApprovalName() (*approvalName, error) { + pc := make([]uintptr, 100) + count := runtime.Callers(0, pc) + + i := 0 + var lastFunc *runtime.Func + + for ; i < count; i++ { + lastFunc = runtime.FuncForPC(pc[i]) + if isTestRunner(lastFunc) { + break + } + } + + if i == 0 || !isTestRunner(lastFunc) { + return nil, fmt.Errorf("approvals: could not find the test method") + } + + testMethod := runtime.FuncForPC(pc[i-1]) + return newApprovalName(pc[i-1], testMethod) +} + +func isTestRunner(f *runtime.Func) bool { + return f != nil && f.Name() == "testing.tRunner" +} + +func (s *approvalName) compare(approvalFile, receivedFile string, reader io.Reader) error { + received, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + + // Ideally, this should only be written if + // 1. the approval file does not exist + // 2. the results differ + err = s.dumpReceivedTestResult(received, receivedFile) + if err != nil { + return err + } + + fh, err := os.Open(approvalFile) + if err != nil { + return err + } + defer fh.Close() + + approved, err := ioutil.ReadAll(fh) + if err != nil { + return err + } + + received = s.normalizeLineEndings(received) + approved = s.normalizeLineEndings(approved) + + // The two sides are identical, nothing more to do. + if bytes.Compare(received, approved) == 0 { + return nil + } + + return fmt.Errorf("failed to approved %s", s.name) +} + +func (s *approvalName) normalizeLineEndings(bs []byte) []byte { + return bytes.Replace(bs, []byte("\r\n"), []byte("\n"), -1) +} + +func (s *approvalName) dumpReceivedTestResult(bs []byte, receivedFile string) error { + err := ioutil.WriteFile(receivedFile, bs, 0644) + + return err +} + +func (s *approvalName) getFileName(extWithDot string, suffix string) string { + if !strings.HasPrefix(extWithDot, ".") { + extWithDot = fmt.Sprintf(".%s", extWithDot) + } + + baseName := path.Base(s.fileName) + baseWithoutExt := baseName[:len(baseName)-len(path.Ext(s.fileName))] + + return fmt.Sprintf("%s.%s.%s%s", baseWithoutExt, s.name, suffix, extWithDot) +} + +func (s *approvalName) getReceivedFile(extWithDot string) string { + return s.getFileName(extWithDot, "received") +} + +func (s *approvalName) getApprovalFile(extWithDot string) string { + return s.getFileName(extWithDot, "approved") +} diff --git a/vendor/github.com/approvals/go-approval-tests/approvals.go b/vendor/github.com/approvals/go-approval-tests/approvals.go new file mode 100644 index 0000000..f4b3a83 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals.go @@ -0,0 +1,204 @@ +package approvaltests + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "strings" + + "encoding/xml" + "github.com/approvals/go-approval-tests/reporters" + "github.com/approvals/go-approval-tests/utils" + "reflect" +) + +var ( + defaultReporter = reporters.NewDiffReporter() + defaultFrontLoadedReporter = reporters.NewFrontLoadedReporter() +) + +// Failable is an interface wrapper around testing.T +type Failable interface { + Fail() +} + +// VerifyWithExtension Example: +// VerifyWithExtension(t, strings.NewReader("Hello"), ".txt") +func VerifyWithExtension(t Failable, reader io.Reader, extWithDot string) error { + namer, err := getApprovalName() + if err != nil { + return err + } + + reporter := getReporter() + err = namer.compare(namer.getApprovalFile(extWithDot), namer.getReceivedFile(extWithDot), reader) + if err != nil { + reporter.Report(namer.getApprovalFile(extWithDot), namer.getReceivedFile(extWithDot)) + t.Fail() + } else { + os.Remove(namer.getReceivedFile(extWithDot)) + } + + return err +} + +// Verify Example: +// Verify(t, strings.NewReader("Hello")) +func Verify(t Failable, reader io.Reader) error { + return VerifyWithExtension(t, reader, ".txt") +} + +// VerifyString Example: +// VerifyString(t, "Hello") +func VerifyString(t Failable, s string) error { + reader := strings.NewReader(s) + return Verify(t, reader) +} + +// VerifyXMLStruct Example: +// VerifyXMLStruct(t, xml) +func VerifyXMLStruct(t Failable, obj interface{}) error { + xmlb, err := xml.MarshalIndent(obj, "", " ") + if err != nil { + tip := "" + if reflect.TypeOf(obj).Name() == "" { + tip = "when using anonymous types be sure to include\n XMLName xml.Name `xml:\"Your_Name_Here\"`\n" + } + message := fmt.Sprintf("error while pretty printing XML\n%verror:\n %v\nXML:\n %v\n", tip, err, obj) + return VerifyWithExtension(t, strings.NewReader(message), ".xml") + } + + return VerifyWithExtension(t, bytes.NewReader(xmlb), ".xml") +} + +// VerifyXMLBytes Example: +// VerifyXMLBytes(t, []byte("")) +func VerifyXMLBytes(t Failable, bs []byte) error { + type node struct { + Attr []xml.Attr + XMLName xml.Name + Children []node `xml:",any"` + Text string `xml:",chardata"` + } + x := node{} + + err := xml.Unmarshal(bs, &x) + if err != nil { + message := fmt.Sprintf("error while parsing XML\nerror:\n %s\nXML:\n %s\n", err, string(bs)) + return VerifyWithExtension(t, strings.NewReader(message), ".xml") + } + + return VerifyXMLStruct(t, x) +} + +// VerifyJSONStruct Example: +// VerifyJSONStruct(t, json) +func VerifyJSONStruct(t Failable, obj interface{}) error { + jsonb, err := json.MarshalIndent(obj, "", " ") + if err != nil { + message := fmt.Sprintf("error while pretty printing JSON\nerror:\n %s\nJSON:\n %s\n", err, obj) + return VerifyWithExtension(t, strings.NewReader(message), ".json") + } + + return VerifyWithExtension(t, bytes.NewReader(jsonb), ".json") +} + +// VerifyJSONBytes Example: +// VerifyJSONBytes(t, []byte("{ \"Greeting\": \"Hello\" }")) +func VerifyJSONBytes(t Failable, bs []byte) error { + var obj map[string]interface{} + err := json.Unmarshal(bs, &obj) + if err != nil { + message := fmt.Sprintf("error while parsing JSON\nerror:\n %s\nJSON:\n %s\n", err, string(bs)) + return VerifyWithExtension(t, strings.NewReader(message), ".json") + } + + return VerifyJSONStruct(t, obj) +} + +// VerifyMap Example: +// VerifyMap(t, map[string][string] { "dog": "bark" }) +func VerifyMap(t Failable, m interface{}) error { + outputText := utils.PrintMap(m) + return VerifyString(t, outputText) +} + +// VerifyArray Example: +// VerifyArray(t, []string{"dog", "cat"}) +func VerifyArray(t Failable, array interface{}) error { + outputText := utils.PrintArray(array) + return VerifyString(t, outputText) +} + +// VerifyAll Example: +// VerifyAll(t, "uppercase", []string("dog", "cat"}, func(x interface{}) string { return strings.ToUpper(x.(string)) }) +func VerifyAll(t Failable, header string, collection interface{}, transform func(interface{}) string) error { + if len(header) != 0 { + header = fmt.Sprintf("%s\n\n\n", header) + } + + outputText := header + strings.Join(utils.MapToString(collection, transform), "\n") + return VerifyString(t, outputText) +} + +type reporterCloser struct { + reporter *reporters.Reporter +} + +func (s *reporterCloser) Close() error { + defaultReporter = s.reporter + return nil +} + +type frontLoadedReporterCloser struct { + reporter *reporters.Reporter +} + +func (s *frontLoadedReporterCloser) Close() error { + defaultFrontLoadedReporter = s.reporter + return nil +} + +// UseReporter configures which reporter to use on failure. +// Add at the test or method level to configure your reporter. +// +// The following examples shows how to use a reporter for all of your test cases +// in a package directory through go's setup feature. +// +// +// func TestMain(m *testing.M) { +// r := UseReporter(reporters.NewBeyondCompareReporter()) +// defer r.Close() +// +// os.Exit(m.Run()) +// } +// +func UseReporter(reporter reporters.Reporter) io.Closer { + closer := &reporterCloser{ + reporter: defaultReporter, + } + + defaultReporter = &reporter + return closer +} + +// UseFrontLoadedReporter configures reporters ahead of all other reporters to +// handle situations like CI. These reporters usually prevent reporting in +// scenarios that are headless. +func UseFrontLoadedReporter(reporter reporters.Reporter) io.Closer { + closer := &frontLoadedReporterCloser{ + reporter: defaultFrontLoadedReporter, + } + + defaultFrontLoadedReporter = &reporter + return closer +} + +func getReporter() reporters.Reporter { + return reporters.NewFirstWorkingReporter( + *defaultFrontLoadedReporter, + *defaultReporter, + ) +} diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestReporterFromSetup.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestReporterFromSetup.approved.txt new file mode 100644 index 0000000..c57eff5 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestReporterFromSetup.approved.txt @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsFor1.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsFor1.approved.txt new file mode 100644 index 0000000..3ba9ad1 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsFor1.approved.txt @@ -0,0 +1,5 @@ +uppercase + + +[Christopher] => CHRISTOPHER +[Llewellyn] => LLEWELLYN \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsFor2.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsFor2.approved.txt new file mode 100644 index 0000000..d0ba3fa --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsFor2.approved.txt @@ -0,0 +1,7 @@ +character at + + +[Christopher,0] => C +[Christopher,1] => h +[Llewellyn,0] => L +[Llewellyn,1] => l \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsFor9.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsFor9.approved.txt new file mode 100644 index 0000000..e5ed07e --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsFor9.approved.txt @@ -0,0 +1,259 @@ +sum numbers + + +[Christopher,0,2,4,6,8,10,12,14] => Christopher[56] +[Christopher,0,2,4,6,8,10,12,15] => Christopher[57] +[Christopher,0,2,4,6,8,10,13,14] => Christopher[57] +[Christopher,0,2,4,6,8,10,13,15] => Christopher[58] +[Christopher,0,2,4,6,8,11,12,14] => Christopher[57] +[Christopher,0,2,4,6,8,11,12,15] => Christopher[58] +[Christopher,0,2,4,6,8,11,13,14] => Christopher[58] +[Christopher,0,2,4,6,8,11,13,15] => Christopher[59] +[Christopher,0,2,4,6,9,10,12,14] => Christopher[57] +[Christopher,0,2,4,6,9,10,12,15] => Christopher[58] +[Christopher,0,2,4,6,9,10,13,14] => Christopher[58] +[Christopher,0,2,4,6,9,10,13,15] => Christopher[59] +[Christopher,0,2,4,6,9,11,12,14] => Christopher[58] +[Christopher,0,2,4,6,9,11,12,15] => Christopher[59] +[Christopher,0,2,4,6,9,11,13,14] => Christopher[59] +[Christopher,0,2,4,6,9,11,13,15] => Christopher[60] +[Christopher,0,2,4,7,8,10,12,14] => Christopher[57] +[Christopher,0,2,4,7,8,10,12,15] => Christopher[58] +[Christopher,0,2,4,7,8,10,13,14] => Christopher[58] +[Christopher,0,2,4,7,8,10,13,15] => Christopher[59] +[Christopher,0,2,4,7,8,11,12,14] => Christopher[58] +[Christopher,0,2,4,7,8,11,12,15] => Christopher[59] +[Christopher,0,2,4,7,8,11,13,14] => Christopher[59] +[Christopher,0,2,4,7,8,11,13,15] => Christopher[60] +[Christopher,0,2,4,7,9,10,12,14] => Christopher[58] +[Christopher,0,2,4,7,9,10,12,15] => Christopher[59] +[Christopher,0,2,4,7,9,10,13,14] => Christopher[59] +[Christopher,0,2,4,7,9,10,13,15] => Christopher[60] +[Christopher,0,2,4,7,9,11,12,14] => Christopher[59] +[Christopher,0,2,4,7,9,11,12,15] => Christopher[60] +[Christopher,0,2,4,7,9,11,13,14] => Christopher[60] +[Christopher,0,2,4,7,9,11,13,15] => Christopher[61] +[Christopher,0,2,5,6,8,10,12,14] => Christopher[57] +[Christopher,0,2,5,6,8,10,12,15] => Christopher[58] +[Christopher,0,2,5,6,8,10,13,14] => Christopher[58] +[Christopher,0,2,5,6,8,10,13,15] => Christopher[59] +[Christopher,0,2,5,6,8,11,12,14] => Christopher[58] +[Christopher,0,2,5,6,8,11,12,15] => Christopher[59] +[Christopher,0,2,5,6,8,11,13,14] => Christopher[59] +[Christopher,0,2,5,6,8,11,13,15] => Christopher[60] +[Christopher,0,2,5,6,9,10,12,14] => Christopher[58] +[Christopher,0,2,5,6,9,10,12,15] => Christopher[59] +[Christopher,0,2,5,6,9,10,13,14] => Christopher[59] +[Christopher,0,2,5,6,9,10,13,15] => Christopher[60] +[Christopher,0,2,5,6,9,11,12,14] => Christopher[59] +[Christopher,0,2,5,6,9,11,12,15] => Christopher[60] +[Christopher,0,2,5,6,9,11,13,14] => Christopher[60] +[Christopher,0,2,5,6,9,11,13,15] => Christopher[61] +[Christopher,0,2,5,7,8,10,12,14] => Christopher[58] +[Christopher,0,2,5,7,8,10,12,15] => Christopher[59] +[Christopher,0,2,5,7,8,10,13,14] => Christopher[59] +[Christopher,0,2,5,7,8,10,13,15] => Christopher[60] +[Christopher,0,2,5,7,8,11,12,14] => Christopher[59] +[Christopher,0,2,5,7,8,11,12,15] => Christopher[60] +[Christopher,0,2,5,7,8,11,13,14] => Christopher[60] +[Christopher,0,2,5,7,8,11,13,15] => Christopher[61] +[Christopher,0,2,5,7,9,10,12,14] => Christopher[59] +[Christopher,0,2,5,7,9,10,12,15] => Christopher[60] +[Christopher,0,2,5,7,9,10,13,14] => Christopher[60] +[Christopher,0,2,5,7,9,10,13,15] => Christopher[61] +[Christopher,0,2,5,7,9,11,12,14] => Christopher[60] +[Christopher,0,2,5,7,9,11,12,15] => Christopher[61] +[Christopher,0,2,5,7,9,11,13,14] => Christopher[61] +[Christopher,0,2,5,7,9,11,13,15] => Christopher[62] +[Christopher,0,3,4,6,8,10,12,14] => Christopher[57] +[Christopher,0,3,4,6,8,10,12,15] => Christopher[58] +[Christopher,0,3,4,6,8,10,13,14] => Christopher[58] +[Christopher,0,3,4,6,8,10,13,15] => Christopher[59] +[Christopher,0,3,4,6,8,11,12,14] => Christopher[58] +[Christopher,0,3,4,6,8,11,12,15] => Christopher[59] +[Christopher,0,3,4,6,8,11,13,14] => Christopher[59] +[Christopher,0,3,4,6,8,11,13,15] => Christopher[60] +[Christopher,0,3,4,6,9,10,12,14] => Christopher[58] +[Christopher,0,3,4,6,9,10,12,15] => Christopher[59] +[Christopher,0,3,4,6,9,10,13,14] => Christopher[59] +[Christopher,0,3,4,6,9,10,13,15] => Christopher[60] +[Christopher,0,3,4,6,9,11,12,14] => Christopher[59] +[Christopher,0,3,4,6,9,11,12,15] => Christopher[60] +[Christopher,0,3,4,6,9,11,13,14] => Christopher[60] +[Christopher,0,3,4,6,9,11,13,15] => Christopher[61] +[Christopher,0,3,4,7,8,10,12,14] => Christopher[58] +[Christopher,0,3,4,7,8,10,12,15] => Christopher[59] +[Christopher,0,3,4,7,8,10,13,14] => Christopher[59] +[Christopher,0,3,4,7,8,10,13,15] => Christopher[60] +[Christopher,0,3,4,7,8,11,12,14] => Christopher[59] +[Christopher,0,3,4,7,8,11,12,15] => Christopher[60] +[Christopher,0,3,4,7,8,11,13,14] => Christopher[60] +[Christopher,0,3,4,7,8,11,13,15] => Christopher[61] +[Christopher,0,3,4,7,9,10,12,14] => Christopher[59] +[Christopher,0,3,4,7,9,10,12,15] => Christopher[60] +[Christopher,0,3,4,7,9,10,13,14] => Christopher[60] +[Christopher,0,3,4,7,9,10,13,15] => Christopher[61] +[Christopher,0,3,4,7,9,11,12,14] => Christopher[60] +[Christopher,0,3,4,7,9,11,12,15] => Christopher[61] +[Christopher,0,3,4,7,9,11,13,14] => Christopher[61] +[Christopher,0,3,4,7,9,11,13,15] => Christopher[62] +[Christopher,0,3,5,6,8,10,12,14] => Christopher[58] +[Christopher,0,3,5,6,8,10,12,15] => Christopher[59] +[Christopher,0,3,5,6,8,10,13,14] => Christopher[59] +[Christopher,0,3,5,6,8,10,13,15] => Christopher[60] +[Christopher,0,3,5,6,8,11,12,14] => Christopher[59] +[Christopher,0,3,5,6,8,11,12,15] => Christopher[60] +[Christopher,0,3,5,6,8,11,13,14] => Christopher[60] +[Christopher,0,3,5,6,8,11,13,15] => Christopher[61] +[Christopher,0,3,5,6,9,10,12,14] => Christopher[59] +[Christopher,0,3,5,6,9,10,12,15] => Christopher[60] +[Christopher,0,3,5,6,9,10,13,14] => Christopher[60] +[Christopher,0,3,5,6,9,10,13,15] => Christopher[61] +[Christopher,0,3,5,6,9,11,12,14] => Christopher[60] +[Christopher,0,3,5,6,9,11,12,15] => Christopher[61] +[Christopher,0,3,5,6,9,11,13,14] => Christopher[61] +[Christopher,0,3,5,6,9,11,13,15] => Christopher[62] +[Christopher,0,3,5,7,8,10,12,14] => Christopher[59] +[Christopher,0,3,5,7,8,10,12,15] => Christopher[60] +[Christopher,0,3,5,7,8,10,13,14] => Christopher[60] +[Christopher,0,3,5,7,8,10,13,15] => Christopher[61] +[Christopher,0,3,5,7,8,11,12,14] => Christopher[60] +[Christopher,0,3,5,7,8,11,12,15] => Christopher[61] +[Christopher,0,3,5,7,8,11,13,14] => Christopher[61] +[Christopher,0,3,5,7,8,11,13,15] => Christopher[62] +[Christopher,0,3,5,7,9,10,12,14] => Christopher[60] +[Christopher,0,3,5,7,9,10,12,15] => Christopher[61] +[Christopher,0,3,5,7,9,10,13,14] => Christopher[61] +[Christopher,0,3,5,7,9,10,13,15] => Christopher[62] +[Christopher,0,3,5,7,9,11,12,14] => Christopher[61] +[Christopher,0,3,5,7,9,11,12,15] => Christopher[62] +[Christopher,0,3,5,7,9,11,13,14] => Christopher[62] +[Christopher,0,3,5,7,9,11,13,15] => Christopher[63] +[Christopher,1,2,4,6,8,10,12,14] => Christopher[57] +[Christopher,1,2,4,6,8,10,12,15] => Christopher[58] +[Christopher,1,2,4,6,8,10,13,14] => Christopher[58] +[Christopher,1,2,4,6,8,10,13,15] => Christopher[59] +[Christopher,1,2,4,6,8,11,12,14] => Christopher[58] +[Christopher,1,2,4,6,8,11,12,15] => Christopher[59] +[Christopher,1,2,4,6,8,11,13,14] => Christopher[59] +[Christopher,1,2,4,6,8,11,13,15] => Christopher[60] +[Christopher,1,2,4,6,9,10,12,14] => Christopher[58] +[Christopher,1,2,4,6,9,10,12,15] => Christopher[59] +[Christopher,1,2,4,6,9,10,13,14] => Christopher[59] +[Christopher,1,2,4,6,9,10,13,15] => Christopher[60] +[Christopher,1,2,4,6,9,11,12,14] => Christopher[59] +[Christopher,1,2,4,6,9,11,12,15] => Christopher[60] +[Christopher,1,2,4,6,9,11,13,14] => Christopher[60] +[Christopher,1,2,4,6,9,11,13,15] => Christopher[61] +[Christopher,1,2,4,7,8,10,12,14] => Christopher[58] +[Christopher,1,2,4,7,8,10,12,15] => Christopher[59] +[Christopher,1,2,4,7,8,10,13,14] => Christopher[59] +[Christopher,1,2,4,7,8,10,13,15] => Christopher[60] +[Christopher,1,2,4,7,8,11,12,14] => Christopher[59] +[Christopher,1,2,4,7,8,11,12,15] => Christopher[60] +[Christopher,1,2,4,7,8,11,13,14] => Christopher[60] +[Christopher,1,2,4,7,8,11,13,15] => Christopher[61] +[Christopher,1,2,4,7,9,10,12,14] => Christopher[59] +[Christopher,1,2,4,7,9,10,12,15] => Christopher[60] +[Christopher,1,2,4,7,9,10,13,14] => Christopher[60] +[Christopher,1,2,4,7,9,10,13,15] => Christopher[61] +[Christopher,1,2,4,7,9,11,12,14] => Christopher[60] +[Christopher,1,2,4,7,9,11,12,15] => Christopher[61] +[Christopher,1,2,4,7,9,11,13,14] => Christopher[61] +[Christopher,1,2,4,7,9,11,13,15] => Christopher[62] +[Christopher,1,2,5,6,8,10,12,14] => Christopher[58] +[Christopher,1,2,5,6,8,10,12,15] => Christopher[59] +[Christopher,1,2,5,6,8,10,13,14] => Christopher[59] +[Christopher,1,2,5,6,8,10,13,15] => Christopher[60] +[Christopher,1,2,5,6,8,11,12,14] => Christopher[59] +[Christopher,1,2,5,6,8,11,12,15] => Christopher[60] +[Christopher,1,2,5,6,8,11,13,14] => Christopher[60] +[Christopher,1,2,5,6,8,11,13,15] => Christopher[61] +[Christopher,1,2,5,6,9,10,12,14] => Christopher[59] +[Christopher,1,2,5,6,9,10,12,15] => Christopher[60] +[Christopher,1,2,5,6,9,10,13,14] => Christopher[60] +[Christopher,1,2,5,6,9,10,13,15] => Christopher[61] +[Christopher,1,2,5,6,9,11,12,14] => Christopher[60] +[Christopher,1,2,5,6,9,11,12,15] => Christopher[61] +[Christopher,1,2,5,6,9,11,13,14] => Christopher[61] +[Christopher,1,2,5,6,9,11,13,15] => Christopher[62] +[Christopher,1,2,5,7,8,10,12,14] => Christopher[59] +[Christopher,1,2,5,7,8,10,12,15] => Christopher[60] +[Christopher,1,2,5,7,8,10,13,14] => Christopher[60] +[Christopher,1,2,5,7,8,10,13,15] => Christopher[61] +[Christopher,1,2,5,7,8,11,12,14] => Christopher[60] +[Christopher,1,2,5,7,8,11,12,15] => Christopher[61] +[Christopher,1,2,5,7,8,11,13,14] => Christopher[61] +[Christopher,1,2,5,7,8,11,13,15] => Christopher[62] +[Christopher,1,2,5,7,9,10,12,14] => Christopher[60] +[Christopher,1,2,5,7,9,10,12,15] => Christopher[61] +[Christopher,1,2,5,7,9,10,13,14] => Christopher[61] +[Christopher,1,2,5,7,9,10,13,15] => Christopher[62] +[Christopher,1,2,5,7,9,11,12,14] => Christopher[61] +[Christopher,1,2,5,7,9,11,12,15] => Christopher[62] +[Christopher,1,2,5,7,9,11,13,14] => Christopher[62] +[Christopher,1,2,5,7,9,11,13,15] => Christopher[63] +[Christopher,1,3,4,6,8,10,12,14] => Christopher[58] +[Christopher,1,3,4,6,8,10,12,15] => Christopher[59] +[Christopher,1,3,4,6,8,10,13,14] => Christopher[59] +[Christopher,1,3,4,6,8,10,13,15] => Christopher[60] +[Christopher,1,3,4,6,8,11,12,14] => Christopher[59] +[Christopher,1,3,4,6,8,11,12,15] => Christopher[60] +[Christopher,1,3,4,6,8,11,13,14] => Christopher[60] +[Christopher,1,3,4,6,8,11,13,15] => Christopher[61] +[Christopher,1,3,4,6,9,10,12,14] => Christopher[59] +[Christopher,1,3,4,6,9,10,12,15] => Christopher[60] +[Christopher,1,3,4,6,9,10,13,14] => Christopher[60] +[Christopher,1,3,4,6,9,10,13,15] => Christopher[61] +[Christopher,1,3,4,6,9,11,12,14] => Christopher[60] +[Christopher,1,3,4,6,9,11,12,15] => Christopher[61] +[Christopher,1,3,4,6,9,11,13,14] => Christopher[61] +[Christopher,1,3,4,6,9,11,13,15] => Christopher[62] +[Christopher,1,3,4,7,8,10,12,14] => Christopher[59] +[Christopher,1,3,4,7,8,10,12,15] => Christopher[60] +[Christopher,1,3,4,7,8,10,13,14] => Christopher[60] +[Christopher,1,3,4,7,8,10,13,15] => Christopher[61] +[Christopher,1,3,4,7,8,11,12,14] => Christopher[60] +[Christopher,1,3,4,7,8,11,12,15] => Christopher[61] +[Christopher,1,3,4,7,8,11,13,14] => Christopher[61] +[Christopher,1,3,4,7,8,11,13,15] => Christopher[62] +[Christopher,1,3,4,7,9,10,12,14] => Christopher[60] +[Christopher,1,3,4,7,9,10,12,15] => Christopher[61] +[Christopher,1,3,4,7,9,10,13,14] => Christopher[61] +[Christopher,1,3,4,7,9,10,13,15] => Christopher[62] +[Christopher,1,3,4,7,9,11,12,14] => Christopher[61] +[Christopher,1,3,4,7,9,11,12,15] => Christopher[62] +[Christopher,1,3,4,7,9,11,13,14] => Christopher[62] +[Christopher,1,3,4,7,9,11,13,15] => Christopher[63] +[Christopher,1,3,5,6,8,10,12,14] => Christopher[59] +[Christopher,1,3,5,6,8,10,12,15] => Christopher[60] +[Christopher,1,3,5,6,8,10,13,14] => Christopher[60] +[Christopher,1,3,5,6,8,10,13,15] => Christopher[61] +[Christopher,1,3,5,6,8,11,12,14] => Christopher[60] +[Christopher,1,3,5,6,8,11,12,15] => Christopher[61] +[Christopher,1,3,5,6,8,11,13,14] => Christopher[61] +[Christopher,1,3,5,6,8,11,13,15] => Christopher[62] +[Christopher,1,3,5,6,9,10,12,14] => Christopher[60] +[Christopher,1,3,5,6,9,10,12,15] => Christopher[61] +[Christopher,1,3,5,6,9,10,13,14] => Christopher[61] +[Christopher,1,3,5,6,9,10,13,15] => Christopher[62] +[Christopher,1,3,5,6,9,11,12,14] => Christopher[61] +[Christopher,1,3,5,6,9,11,12,15] => Christopher[62] +[Christopher,1,3,5,6,9,11,13,14] => Christopher[62] +[Christopher,1,3,5,6,9,11,13,15] => Christopher[63] +[Christopher,1,3,5,7,8,10,12,14] => Christopher[60] +[Christopher,1,3,5,7,8,10,12,15] => Christopher[61] +[Christopher,1,3,5,7,8,10,13,14] => Christopher[61] +[Christopher,1,3,5,7,8,10,13,15] => Christopher[62] +[Christopher,1,3,5,7,8,11,12,14] => Christopher[61] +[Christopher,1,3,5,7,8,11,12,15] => Christopher[62] +[Christopher,1,3,5,7,8,11,13,14] => Christopher[62] +[Christopher,1,3,5,7,8,11,13,15] => Christopher[63] +[Christopher,1,3,5,7,9,10,12,14] => Christopher[61] +[Christopher,1,3,5,7,9,10,12,15] => Christopher[62] +[Christopher,1,3,5,7,9,10,13,14] => Christopher[62] +[Christopher,1,3,5,7,9,10,13,15] => Christopher[63] +[Christopher,1,3,5,7,9,11,12,14] => Christopher[62] +[Christopher,1,3,5,7,9,11,12,15] => Christopher[63] +[Christopher,1,3,5,7,9,11,13,14] => Christopher[63] +[Christopher,1,3,5,7,9,11,13,15] => Christopher[64] \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsForSkipped.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsForSkipped.approved.txt new file mode 100644 index 0000000..5a8f4c3 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyAllCombinationsForSkipped.approved.txt @@ -0,0 +1,10 @@ +skipped divisible by 3 + + +[1] => 1 +[2] => 2 +[4] => 4 +[5] => 5 +[7] => 7 +[8] => 8 +[10] => 10 \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArray.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArray.approved.txt new file mode 100644 index 0000000..da3e1dd --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArray.approved.txt @@ -0,0 +1,3 @@ +[0]=dog +[1]=cat +[2]=bird \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArrayBadArray.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArrayBadArray.approved.txt new file mode 100644 index 0000000..871a226 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArrayBadArray.approved.txt @@ -0,0 +1,3 @@ +error while printing array +received a string + string diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArrayEmptyArray.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArrayEmptyArray.approved.txt new file mode 100644 index 0000000..17a8305 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArrayEmptyArray.approved.txt @@ -0,0 +1 @@ +len(array) == 0 \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArrayTransformation.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArrayTransformation.approved.txt new file mode 100644 index 0000000..ab60bde --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyArrayTransformation.approved.txt @@ -0,0 +1,5 @@ +uppercase + + +Christopher => CHRISTOPHER +Llewellyn => LLEWELLYN \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyBadJSONBytes.approved.json b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyBadJSONBytes.approved.json new file mode 100644 index 0000000..83b1447 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyBadJSONBytes.approved.json @@ -0,0 +1,5 @@ +error while parsing JSON +error: + invalid character 'f' looking for beginning of object key string +JSON: + { foo: "bar", "age": 42, "bark": "woof" } diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyBadXMLBytes.approved.xml b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyBadXMLBytes.approved.xml new file mode 100644 index 0000000..43dde05 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyBadXMLBytes.approved.xml @@ -0,0 +1,5 @@ +error while parsing XML +error: + XML syntax error on line 1: unexpected end element +XML: + Test> diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyBadXMLStruct.approved.xml b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyBadXMLStruct.approved.xml new file mode 100644 index 0000000..33e85da --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyBadXMLStruct.approved.xml @@ -0,0 +1,7 @@ +error while pretty printing XML +when using anonymous types be sure to include + XMLName xml.Name `xml:"Your_Name_Here"` +error: + xml: unsupported type: struct { Title string } +XML: + {Hello World!} diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyJSONBytes.approved.json b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyJSONBytes.approved.json new file mode 100644 index 0000000..4b3c1b1 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyJSONBytes.approved.json @@ -0,0 +1,5 @@ +{ + "age": 42, + "bark": "woof", + "foo": "bar" +} \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyJSONStruct.approved.json b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyJSONStruct.approved.json new file mode 100644 index 0000000..4d8edbe --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyJSONStruct.approved.json @@ -0,0 +1,5 @@ +{ + "Title": "Hello World!", + "Name": "Peter Pan", + "Age": 100 +} \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyMap.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyMap.approved.txt new file mode 100644 index 0000000..1e5c312 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyMap.approved.txt @@ -0,0 +1,2 @@ +[cat]=meow +[dog]=bark \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyMapBadMap.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyMapBadMap.approved.txt new file mode 100644 index 0000000..a353125 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyMapBadMap.approved.txt @@ -0,0 +1,3 @@ +error while printing map +received a string + foo diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyMapEmptyMap.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyMapEmptyMap.approved.txt new file mode 100644 index 0000000..9061572 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyMapEmptyMap.approved.txt @@ -0,0 +1 @@ +len(map) == 0 \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyStringApproval.approved.txt b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyStringApproval.approved.txt new file mode 100644 index 0000000..c57eff5 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyStringApproval.approved.txt @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyXMLBytes.approved.xml b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyXMLBytes.approved.xml new file mode 100644 index 0000000..7454ab2 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyXMLBytes.approved.xml @@ -0,0 +1,5 @@ + + Hello World! + Peter Pan + 100 + \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyXMLStruct.approved.xml b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyXMLStruct.approved.xml new file mode 100644 index 0000000..7454ab2 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/approvals_test.TestVerifyXMLStruct.approved.xml @@ -0,0 +1,5 @@ + + Hello World! + Peter Pan + 100 + \ No newline at end of file diff --git a/vendor/github.com/approvals/go-approval-tests/combination_approvals.go b/vendor/github.com/approvals/go-approval-tests/combination_approvals.go new file mode 100644 index 0000000..9c775a8 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/combination_approvals.go @@ -0,0 +1,296 @@ +package approvaltests + +import ( + "fmt" + "strings" + + "reflect" +) + +type emptyType struct{} + +var ( + // SkipThisCombination should be returned if you do not want to process a particular combination + SkipThisCombination = "♬ SKIP THIS COMBINATION ♬" + + empty = emptyType{} + emptyCollection = []emptyType{empty} +) + +// VerifyAllCombinationsFor1 Example: +// VerifyAllCombinationsFor1(t, "uppercase", func(x interface{}) string { return strings.ToUpper(x.(string)) }, []string("dog", "cat"}) +func VerifyAllCombinationsFor1(t Failable, header string, transform func(interface{}) string, collection1 interface{}) error { + transform2 := func(p1, p2, p3, p4, p5, p6, p7, p8, p9 interface{}) string { + return transform(p1) + } + + return VerifyAllCombinationsFor9(t, header, transform2, collection1, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection) +} + +// VerifyAllCombinationsFor2 Example: +// VerifyAllCombinationsFor2(t, "uppercase", func(x interface{}) string { return strings.ToUpper(x.(string)) }, []string("dog", "cat"}, []int{1,2) +func VerifyAllCombinationsFor2(t Failable, header string, transform func(interface{}, interface{}) string, collection1 interface{}, collection2 interface{}) error { + transform2 := func(p1, p2, p3, p4, p5, p6, p7, p8, p9 interface{}) string { + return transform(p1, p2) + } + + return VerifyAllCombinationsFor9(t, header, transform2, collection1, + collection2, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection) +} + +// VerifyAllCombinationsFor3 is for combinations of 3. +func VerifyAllCombinationsFor3( + t Failable, + header string, + transform func(p1, p2, p3 interface{}) string, + collection1, collection2, collection3 interface{}) error { + + kerning := func(p1, p2, p3, p4, p5, p6, p7, p8, p9 interface{}) string { + return transform(p1, p2, p3) + } + + return VerifyAllCombinationsFor9(t, header, kerning, + collection1, + collection2, + collection3, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection) +} + +// VerifyAllCombinationsFor4 is for combinations of 4. +func VerifyAllCombinationsFor4( + t Failable, + header string, + transform func(p1, p2, p3, p4 interface{}) string, + collection1, collection2, collection3, collection4 interface{}) error { + + kerning := func(p1, p2, p3, p4, p5, p6, p7, p8, p9 interface{}) string { + return transform(p1, p2, p3, p4) + } + + return VerifyAllCombinationsFor9(t, header, kerning, + collection1, + collection2, + collection3, + collection4, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection) +} + +// VerifyAllCombinationsFor5 is for combinations of 5. +func VerifyAllCombinationsFor5( + t Failable, + header string, + transform func(p1, p2, p3, p4, p5 interface{}) string, + collection1, collection2, collection3, collection4, collection5 interface{}) error { + + kerning := func(p1, p2, p3, p4, p5, p6, p7, p8, p9 interface{}) string { + return transform(p1, p2, p3, p4, p5) + } + + return VerifyAllCombinationsFor9(t, header, kerning, + collection1, + collection2, + collection3, + collection4, + collection5, + emptyCollection, + emptyCollection, + emptyCollection, + emptyCollection) +} + +// VerifyAllCombinationsFor6 is for combinations of 6. +func VerifyAllCombinationsFor6( + t Failable, + header string, + transform func(p1, p2, p3, p4, p5, p6 interface{}) string, + collection1, collection2, collection3, collection4, collection5, collection6 interface{}) error { + + kerning := func(p1, p2, p3, p4, p5, p6, p7, p8, p9 interface{}) string { + return transform(p1, p2, p3, p4, p5, p6) + } + + return VerifyAllCombinationsFor9(t, header, kerning, + collection1, + collection2, + collection3, + collection4, + collection5, + collection6, + emptyCollection, + emptyCollection, + emptyCollection) +} + +// VerifyAllCombinationsFor7 is for combinations of 7. +func VerifyAllCombinationsFor7( + t Failable, + header string, + transform func(p1, p2, p3, p4, p5, p6, p7 interface{}) string, + collection1, collection2, collection3, collection4, collection5, collection6, collection7 interface{}) error { + + kerning := func(p1, p2, p3, p4, p5, p6, p7, p8, p9 interface{}) string { + return transform(p1, p2, p3, p4, p5, p6, p7) + } + + return VerifyAllCombinationsFor9(t, header, kerning, + collection1, + collection2, + collection3, + collection4, + collection5, + collection6, + collection7, + emptyCollection, + emptyCollection) +} + +// VerifyAllCombinationsFor8 is for combinations of 8. +func VerifyAllCombinationsFor8( + t Failable, + header string, + transform func(p1, p2, p3, p4, p5, p6, p7, p8 interface{}) string, + collection1, collection2, collection3, collection4, collection5, collection6, collection7, collection8 interface{}) error { + + kerning := func(p1, p2, p3, p4, p5, p6, p7, p8, p9 interface{}) string { + return transform(p1, p2, p3, p4, p5, p6, p7, p8) + } + + return VerifyAllCombinationsFor9(t, header, kerning, + collection1, + collection2, + collection3, + collection4, + collection5, + collection6, + collection7, + collection8, + emptyCollection) +} + +// VerifyAllCombinationsFor9 is for combinations of 9. +func VerifyAllCombinationsFor9( + t Failable, + header string, + transform func(a, b, c, d, e, f, g, h, i interface{}) string, + collection1, + collection2, + collection3, + collection4, + collection5, + collection6, + collection7, + collection8, + collection9 interface{}) error { + + if len(header) != 0 { + header = fmt.Sprintf("%s\n\n\n", header) + } + + var mapped []string + + slice1 := reflect.ValueOf(collection1) + slice2 := reflect.ValueOf(collection2) + slice3 := reflect.ValueOf(collection3) + slice4 := reflect.ValueOf(collection4) + slice5 := reflect.ValueOf(collection5) + slice6 := reflect.ValueOf(collection6) + slice7 := reflect.ValueOf(collection7) + slice8 := reflect.ValueOf(collection8) + slice9 := reflect.ValueOf(collection9) + + for i1 := 0; i1 < slice1.Len(); i1++ { + for i2 := 0; i2 < slice2.Len(); i2++ { + for i3 := 0; i3 < slice3.Len(); i3++ { + for i4 := 0; i4 < slice4.Len(); i4++ { + for i5 := 0; i5 < slice5.Len(); i5++ { + for i6 := 0; i6 < slice6.Len(); i6++ { + for i7 := 0; i7 < slice7.Len(); i7++ { + for i8 := 0; i8 < slice8.Len(); i8++ { + for i9 := 0; i9 < slice9.Len(); i9++ { + p1 := slice1.Index(i1).Interface() + p2 := slice2.Index(i2).Interface() + p3 := slice3.Index(i3).Interface() + p4 := slice4.Index(i4).Interface() + p5 := slice5.Index(i5).Interface() + p6 := slice6.Index(i6).Interface() + p7 := slice7.Index(i7).Interface() + p8 := slice8.Index(i8).Interface() + p9 := slice9.Index(i9).Interface() + + parameterText := getParameterText(p1, p2, p3, p4, p5, p6, p7, p8, p9) + transformText := getTransformText(transform, p1, p2, p3, p4, p5, p6, p7, p8, p9) + if transformText != SkipThisCombination { + mapped = append(mapped, fmt.Sprintf("%s => %s", parameterText, transformText)) + } + } + } + } + } + } + } + } + } + } + + outputText := header + strings.Join(mapped, "\n") + return VerifyString(t, outputText) +} + +func getParameterText(args ...interface{}) string { + parameterText := "[" + for _, x := range args { + if x != empty { + parameterText += fmt.Sprintf("%v,", x) + } + } + + parameterText = parameterText[0 : len(parameterText)-1] + parameterText += "]" + + return parameterText +} + +func getTransformText( + transform func(a, b, c, d, e, f, g, h, i interface{}) string, + p1, + p2, + p3, + p4, + p5, + p6, + p7, + p8, + p9 interface{}) (s string) { + defer func() { + r := recover() + if r != nil { + s = "panic occurred" + } + }() + + return transform(p1, p2, p3, p4, p5, p6, p7, p8, p9) +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/all_failing.go b/vendor/github.com/approvals/go-approval-tests/reporters/all_failing.go new file mode 100644 index 0000000..788a1e3 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/all_failing.go @@ -0,0 +1,18 @@ +package reporters + +var ( + clipboardScratchData = "" +) + +type allFailing struct{} + +// NewAllFailingTestReporter copies move file command to your clipboard +func NewAllFailingTestReporter() Reporter { + return &allFailing{} +} + +func (s *allFailing) Report(approved, received string) bool { + move := getMoveCommandText(approved, received) + clipboardScratchData = clipboardScratchData + move + "\n" + return copyToClipboard(clipboardScratchData) +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/beyond_compare.go b/vendor/github.com/approvals/go-approval-tests/reporters/beyond_compare.go new file mode 100644 index 0000000..81dccd2 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/beyond_compare.go @@ -0,0 +1,15 @@ +package reporters + +type beyondCompare struct{} + +// NewBeyondCompareReporter creates a new reporter for Beyond Compare 4. +func NewBeyondCompareReporter() Reporter { + return &beyondCompare{} +} + +func (s *beyondCompare) Report(approved, received string) bool { + xs := []string{received, approved} + programName := "C:/Program Files/Beyond Compare 4/BComp.exe" + + return launchProgram(programName, approved, xs...) +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/clipboard.go b/vendor/github.com/approvals/go-approval-tests/reporters/clipboard.go new file mode 100644 index 0000000..4f4f9ff --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/clipboard.go @@ -0,0 +1,66 @@ +package reporters + +import ( + "fmt" + "os/exec" + "path/filepath" + "runtime" +) + +type clipboard struct{} + +// NewClipboardReporter copies move file command to your clipboard +func NewClipboardReporter() Reporter { + return &clipboard{} +} + +func (s *clipboard) Report(approved, received string) bool { + move := getMoveCommandText(approved, received) + return copyToClipboard(move) +} + +func copyToClipboard(move string) bool { + switch runtime.GOOS { + case "windows": + return copyToWindowsClipboard(move) + default: + return copyToDarwinClipboard(move) + } +} + +func getMoveCommandText(approved, received string) string { + receivedFull, _ := filepath.Abs(received) + approvedFull, _ := filepath.Abs(approved) + + var move string + + switch runtime.GOOS { + case "windows": + move = fmt.Sprintf("move /Y \"%s\" \"%s\"", receivedFull, approvedFull) + default: + move = fmt.Sprintf("mv %s %s", receivedFull, approvedFull) + } + + return move +} +func copyToWindowsClipboard(text string) bool { + return pipeToProgram("clip", text) +} + +func copyToDarwinClipboard(text string) bool { + return pipeToProgram("pbcopy", text) +} + +func pipeToProgram(programName, text string) bool { + c := exec.Command(programName) + pipe, err := c.StdinPipe() + if err != nil { + fmt.Printf("StdinPipe: err=%s", err) + return false + } + pipe.Write([]byte(text)) + pipe.Close() + + c.Start() + return true +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/continuous_integration.go b/vendor/github.com/approvals/go-approval-tests/reporters/continuous_integration.go new file mode 100644 index 0000000..923feb7 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/continuous_integration.go @@ -0,0 +1,28 @@ +package reporters + +import ( + "os" + "strconv" +) + +type continuousIntegration struct{} + +// NewContinuousIntegrationReporter creates a new reporter for CI. +// +// The reporter checks the environment variable CI for a value of true. +func NewContinuousIntegrationReporter() Reporter { + return &continuousIntegration{} +} + +func (s *continuousIntegration) Report(approved, received string) bool { + value, exists := os.LookupEnv("CI") + + if exists { + ci, err := strconv.ParseBool(value) + if err == nil { + return ci + } + } + + return false +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/diff_reporter.go b/vendor/github.com/approvals/go-approval-tests/reporters/diff_reporter.go new file mode 100644 index 0000000..630548e --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/diff_reporter.go @@ -0,0 +1,40 @@ +package reporters + +import ( + "os/exec" + + "github.com/approvals/go-approval-tests/utils" +) + +// NewFrontLoadedReporter creates the default front loaded reporter. +func NewFrontLoadedReporter() *Reporter { + tmp := NewFirstWorkingReporter( + NewContinuousIntegrationReporter(), + ) + + return &tmp +} + +// NewDiffReporter creates the default diff reporter. +func NewDiffReporter() *Reporter { + tmp := NewFirstWorkingReporter( + NewBeyondCompareReporter(), + NewIntelliJReporter(), + NewPrintSupportedDiffProgramsReporter(), + NewQuietReporter(), + ) + + return &tmp +} + +func launchProgram(programName, approved string, args ...string) bool { + if !utils.DoesFileExist(programName) { + return false + } + + utils.EnsureExists(approved) + + cmd := exec.Command(programName, args...) + cmd.Start() + return true +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/file_launcher.go b/vendor/github.com/approvals/go-approval-tests/reporters/file_launcher.go new file mode 100644 index 0000000..cb9bd5b --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/file_launcher.go @@ -0,0 +1,27 @@ +package reporters + +import ( + "os/exec" + "runtime" +) + +type fileLauncher struct{} + +// NewFileLauncherReporter launches registered application of the received file's type only. +func NewFileLauncherReporter() Reporter { + return &fileLauncher{} +} + +func (s *fileLauncher) Report(approved, received string) bool { + var cmd *exec.Cmd + + switch runtime.GOOS { + case "windows": + cmd = exec.Command("cmd", "/C", "start", "Needed Title", received, "/B") + default: + cmd = exec.Command("open", received) + } + + cmd.Start() + return true +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/intellij.go b/vendor/github.com/approvals/go-approval-tests/reporters/intellij.go new file mode 100644 index 0000000..63e9506 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/intellij.go @@ -0,0 +1,15 @@ +package reporters + +type intellij struct{} + +// NewIntelliJReporter creates a new reporter for IntelliJ. +func NewIntelliJReporter() Reporter { + return &intellij{} +} + +func (s *intellij) Report(approved, received string) bool { + xs := []string{"diff", received, approved} + programName := "C:/Program Files (x86)/JetBrains/IntelliJ IDEA 2016/bin/idea.exe" + + return launchProgram(programName, approved, xs...) +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/newbie.go b/vendor/github.com/approvals/go-approval-tests/reporters/newbie.go new file mode 100644 index 0000000..8e662bf --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/newbie.go @@ -0,0 +1,18 @@ +package reporters + +import ( + "fmt" +) + +type printSupportedDiffPrograms struct{} + +// NewPrintSupportedDiffProgramsReporter creates a new reporter that states what reporters are supported. +func NewPrintSupportedDiffProgramsReporter() Reporter { + return &quiet{} +} + +func (s *printSupportedDiffPrograms) Report(approved, received string) bool { + fmt.Printf("no diff reporters found on your system\ncurrently supported reporters are [in order of preference]:\nBeyond Compare\nIntelliJ") + + return false +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/quiet.go b/vendor/github.com/approvals/go-approval-tests/reporters/quiet.go new file mode 100644 index 0000000..a715c0e --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/quiet.go @@ -0,0 +1,29 @@ +package reporters + +import ( + "fmt" + "path/filepath" + + "github.com/approvals/go-approval-tests/utils" +) + +type quiet struct{} + +// NewQuietReporter creates a new reporter that does nothing. +func NewQuietReporter() Reporter { + return &quiet{} +} + +func (s *quiet) Report(approved, received string) bool { + approvedFull, _ := filepath.Abs(approved) + receivedFull, _ := filepath.Abs(received) + + if utils.DoesFileExist(approved) { + fmt.Printf("approval files did not match\napproved: %v\nreceived: %v\n", approvedFull, receivedFull) + + } else { + fmt.Printf("result never approved\napproved: %v\nreceived: %v\n", approvedFull, receivedFull) + } + + return true +} diff --git a/vendor/github.com/approvals/go-approval-tests/reporters/reporter.go b/vendor/github.com/approvals/go-approval-tests/reporters/reporter.go new file mode 100644 index 0000000..99d6866 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/reporters/reporter.go @@ -0,0 +1,53 @@ +package reporters + +// Reporter are called on failing approvals. +type Reporter interface { + // Report is called when the approved and received file do not match. + Report(approved, received string) bool +} + +// FirstWorkingReporter reports using the first possible reporter. +type FirstWorkingReporter struct { + Reporters []Reporter +} + +// Report is called when the approved and received file do not match. +func (s *FirstWorkingReporter) Report(approved, received string) bool { + for _, reporter := range s.Reporters { + result := reporter.Report(approved, received) + if result { + return true + } + } + + return false +} + +// NewFirstWorkingReporter creates in the order reporters are passed in. +func NewFirstWorkingReporter(reporters ...Reporter) Reporter { + return &FirstWorkingReporter{ + Reporters: reporters, + } +} + +// MultiReporter reports all reporters. +type MultiReporter struct { + Reporters []Reporter +} + +// Report is called when the approved and received file do not match. +func (s *MultiReporter) Report(approved, received string) bool { + result := false + for _, reporter := range s.Reporters { + result = reporter.Report(approved, received) || result + } + + return result +} + +// NewMultiReporter calls all reporters. +func NewMultiReporter(reporters ...Reporter) Reporter { + return &MultiReporter{ + Reporters: reporters, + } +} diff --git a/vendor/github.com/approvals/go-approval-tests/utils/collection_utils.go b/vendor/github.com/approvals/go-approval-tests/utils/collection_utils.go new file mode 100644 index 0000000..1602fa6 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/utils/collection_utils.go @@ -0,0 +1,77 @@ +package utils + +import ( + "fmt" + "reflect" + "sort" + "strings" +) + +// PrintMap prints a map +func PrintMap(m interface{}) string { + var outputText string + + v := reflect.ValueOf(m) + if v.Kind() != reflect.Map { + outputText = fmt.Sprintf("error while printing map\nreceived a %T\n %s\n", m, m) + } else { + + keys := v.MapKeys() + var xs []string + + for _, k := range keys { + xs = append(xs, fmt.Sprintf("[%s]=%s", k, v.MapIndex(k))) + } + + sort.Strings(xs) + if len(xs) == 0 { + outputText = "len(map) == 0" + } else { + outputText = strings.Join(xs, "\n") + } + } + + return outputText +} + +// PrintArray prints an array +func PrintArray(m interface{}) string { + var outputText string + + switch reflect.TypeOf(m).Kind() { + case reflect.Slice: + var xs []string + + slice := reflect.ValueOf(m) + for i := 0; i < slice.Len(); i++ { + xs = append(xs, fmt.Sprintf("[%d]=%s", i, slice.Index(i))) + } + + if len(xs) == 0 { + outputText = "len(array) == 0" + } else { + outputText = strings.Join(xs, "\n") + } + default: + outputText = fmt.Sprintf("error while printing array\nreceived a %T\n %s\n", m, m) + } + + return outputText +} + +// MapToString maps a collection to a string collection +func MapToString(collection interface{}, transform func(x interface{}) string) []string { + switch reflect.TypeOf(collection).Kind() { + case reflect.Slice: + var xs []string + + slice := reflect.ValueOf(collection) + for i := 0; i < slice.Len(); i++ { + xs = append(xs, transform(slice.Index(i).Interface())) + } + + return xs + default: + panic(fmt.Sprintf("error while mapping array to string\nreceived a %T\n %s\n", collection, collection)) + } +} diff --git a/vendor/github.com/approvals/go-approval-tests/utils/file_utils.go b/vendor/github.com/approvals/go-approval-tests/utils/file_utils.go new file mode 100644 index 0000000..bec3b95 --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/utils/file_utils.go @@ -0,0 +1,24 @@ +package utils + +import ( + "io/ioutil" + "os" +) + +// DoesFileExist checks if a file exists. +func DoesFileExist(fileName string) bool { + _, err := os.Stat(fileName) + if os.IsNotExist(err) { + return false + } + return true +} + +// EnsureExists creates if the file does not already exist. +func EnsureExists(fileName string) { + if DoesFileExist(fileName) { + return + } + + ioutil.WriteFile(fileName, []byte(""), 0644) +} diff --git a/vendor/github.com/approvals/go-approval-tests/utils/testing_utils.go b/vendor/github.com/approvals/go-approval-tests/utils/testing_utils.go new file mode 100644 index 0000000..c6c9c8a --- /dev/null +++ b/vendor/github.com/approvals/go-approval-tests/utils/testing_utils.go @@ -0,0 +1,11 @@ +package utils + +import "testing" + +// AssertEqual Example: +// AssertEqual(t, 10, number, "number") +func AssertEqual(t *testing.T, expected, actual interface{}, message string) { + if expected != actual { + t.Fatalf(message+"\n[expected != actual]\n[%s != %s]", expected, actual) + } +} diff --git a/versions.go b/versions.go index 941fafc..763aad5 100644 --- a/versions.go +++ b/versions.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "encoding/json" log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/olekukonko/tablewriter" @@ -15,8 +16,31 @@ func listVersions(c *cli.Context) { if err != nil { log.Fatalf("Request failed: %v", err) } + + json := c.Bool(flJSON.Name) + var f func(_ ListVersionsResponse) error + if json { + f = printListVersionsAsJSON + } else { + f = printListVersionsAsTable + } + if err := f(v); err != nil { + log.Fatal(err) + } +} + +func printListVersionsAsJSON(r ListVersionsResponse) error { + b, err := json.MarshalIndent(r.Extensions, "", " ") + if err != nil { + return fmt.Errorf("failed to format as json: %+v", err) + } + fmt.Fprintf(os.Stdout, "%s", string(b)) + return nil +} + +func printListVersionsAsTable(v ListVersionsResponse) error { table := tablewriter.NewWriter(os.Stdout) - table.SetColWidth(100) + table.SetColWidth(4000) table.SetHeader([]string{"Namespace", "Type", "Version", "Replicated?", "Internal?", "Regions"}) data := [][]string{} for _, e := range v.Extensions { @@ -24,4 +48,6 @@ func listVersions(c *cli.Context) { } table.AppendBulk(data) table.Render() + + return nil }