diff --git a/cobracli/execute.go b/cobracli/execute.go index 748ef6dc9..3f02beacf 100644 --- a/cobracli/execute.go +++ b/cobracli/execute.go @@ -5,10 +5,18 @@ package cobracli import ( + "bytes" + "encoding/json" "fmt" + "io" "io/ioutil" + "maps" + "os" + "slices" + "sort" "strings" + werror "github.com/palantir/witchcraft-go-error" "github.com/spf13/cobra" ) @@ -205,3 +213,69 @@ func FlagErrorsUsageErrorConfigurer(command *cobra.Command) { return fmt.Errorf("%s\n%s", err.Error(), strings.TrimSuffix(c.UsageString(), "\n")) }) } + +// PrintInfoLevelErrorAndParamsWithDebugTransformer provides a cobra error handler that prints the error message and +// any werror-attached safe/unsafe params, one per line. Param values are formatted using their JSON-marshalled +// representation; if a value cannot be marshalled, the marshalling error message is printed in its place. +// +// If the debugVar pointer is non-nil and resolves to true, the debugErrTransform function is called to render the +// error instead, and params are not printed separately. Commonly, debugErrTransform is paired with +// errorstringer.StackWithInterleavedMessages to print a stacktrace. +// +// Output is written to errWriter. If errWriter is nil, os.Stderr is used. Note that this handler does not write to +// the cobra command's configured error stream (command.ErrOrStderr()): callers that want to redirect output through +// the command must pass command.ErrOrStderr() (or an equivalent writer) explicitly. Output is buffered and flushed +// in a single write so partial output is not emitted on a write failure. +func PrintInfoLevelErrorAndParamsWithDebugTransformer(errWriter io.Writer, debugVar *bool, debugErrTransform func(error) string) func(command *cobra.Command, err error) { + if errWriter == nil { + errWriter = os.Stderr + } + return func(_ *cobra.Command, err error) { + if err == nil || err.Error() == "" { + return + } + + var buf bytes.Buffer + // always flush buffer no matter what return path from function + defer func() { + _, _ = buf.WriteTo(errWriter) + }() + + if debugVar != nil && *debugVar && debugErrTransform != nil { + // writes to bytes.Buffer always return nil error + _, _ = fmt.Fprintln(&buf, "Error:", debugErrTransform(err)) + return + } + // writes to bytes.Buffer always return nil error + _, _ = fmt.Fprintln(&buf, "Error:", err.Error()) + safe, unsafe := werror.ParamsFromError(err) + if len(safe) == 0 && len(unsafe) == 0 { + return + } + var keys []string + keys = slices.AppendSeq(keys, maps.Keys(safe)) + keys = slices.AppendSeq(keys, maps.Keys(unsafe)) + sort.Strings(keys) + // writes to bytes.Buffer always return nil error + _, _ = fmt.Fprintln(&buf, "Error params:") + for _, key := range keys { + if val, ok := safe[key]; ok { + // writes to bytes.Buffer always return nil error + _, _ = fmt.Fprintln(&buf, formattedParamLine(key, val)) + } + if val, ok := unsafe[key]; ok { + // writes to bytes.Buffer always return nil error + _, _ = fmt.Fprintln(&buf, formattedParamLine(key, val)) + } + } + } +} + +func formattedParamLine(key string, val any) string { + marshalled, err := json.Marshal(val) + formattedVal := string(marshalled) + if err != nil { + formattedVal = fmt.Sprintf("error json marshalling parameter value: %s", err.Error()) + } + return fmt.Sprintf(" %s: %s", key, formattedVal) +} diff --git a/cobracli/execute_test.go b/cobracli/execute_test.go index f34d12f17..b308475c5 100644 --- a/cobracli/execute_test.go +++ b/cobracli/execute_test.go @@ -6,10 +6,12 @@ package cobracli_test import ( "bytes" + "context" "regexp" "testing" "github.com/palantir/pkg/cobracli" + werror "github.com/palantir/witchcraft-go-error" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" @@ -44,7 +46,7 @@ func TestExecuteWithParams(t *testing.T) { { "version command prints version", func(cmd *cobra.Command, args []string) error { - return errors.Errorf("hello-error") + return errors.New("hello-error") }, nil, []string{"version"}, @@ -55,7 +57,7 @@ func TestExecuteWithParams(t *testing.T) { { "version command not present if not requested", func(cmd *cobra.Command, args []string) error { - return errors.Errorf("hello-error") + return errors.New("hello-error") }, nil, []string{"version"}, @@ -67,7 +69,7 @@ func TestExecuteWithParams(t *testing.T) { { "version flag prints version", func(cmd *cobra.Command, args []string) error { - return errors.Errorf("hello-error") + return errors.New("hello-error") }, nil, []string{"--version"}, @@ -78,7 +80,7 @@ func TestExecuteWithParams(t *testing.T) { { "version flag exists if version is set on root command", func(cmd *cobra.Command, args []string) error { - return errors.Errorf("hello-error") + return errors.New("hello-error") }, func(cmd *cobra.Command) { cmd.Version = "1.0.0" @@ -91,7 +93,7 @@ func TestExecuteWithParams(t *testing.T) { { "version flag not present if not requested", func(cmd *cobra.Command, args []string) error { - return errors.Errorf("hello-error") + return errors.New("hello-error") }, nil, []string{"--version"}, @@ -108,7 +110,7 @@ Flags: { "standard fail", func(cmd *cobra.Command, args []string) error { - return errors.Errorf("custom failure") + return errors.New("custom failure") }, nil, nil, @@ -119,7 +121,7 @@ Flags: { "standard fail with package debug variable prints stack trace", func(cmd *cobra.Command, args []string) error { - return errors.Errorf("custom failure") + return errors.New("custom failure") }, nil, []string{ @@ -161,3 +163,52 @@ $`), }() } } + +func TestPrintInfoLevelErrorAndParamsWithDebugTransformer(t *testing.T) { + t.Run("nil error produces no output", func(t *testing.T) { + var out bytes.Buffer + cobracli.PrintInfoLevelErrorAndParamsWithDebugTransformer(&out, nil, nil)(&cobra.Command{}, nil) + require.Equal(t, "", out.String()) + }) + + t.Run("error without params prints only error message", func(t *testing.T) { + var out bytes.Buffer + cobracli.PrintInfoLevelErrorAndParamsWithDebugTransformer(&out, nil, nil)(&cobra.Command{}, errors.New("🥳")) + require.Equal(t, "Error: 🥳\n", out.String()) + }) + + t.Run("error with params prints error and params", func(t *testing.T) { + var out bytes.Buffer + type jsonInvalidMapKey struct{} + cobracli.PrintInfoLevelErrorAndParamsWithDebugTransformer(&out, nil, nil)(&cobra.Command{}, werror.ErrorWithContextParams( + context.Background(), + "🥳", + werror.SafeParam("foo", "bar"), + werror.UnsafeParam("baz", 7), + werror.UnsafeParam("qux", map[jsonInvalidMapKey]string{{}: ""}), + )) + require.Equal(t, `Error: 🥳 +Error params: + baz: 7 + foo: "bar" + qux: error json marshalling parameter value: json: unsupported type: map[cobracli_test.jsonInvalidMapKey]string +`, out.String()) + }) + + t.Run("debug mode uses debug transformer", func(t *testing.T) { + var out bytes.Buffer + cobracli.PrintInfoLevelErrorAndParamsWithDebugTransformer(&out, ptr(true), func(err error) string { + return "✅" + })(&cobra.Command{}, werror.ErrorWithContextParams( + context.Background(), + "🥳", + werror.SafeParam("foo", "bar"), + werror.UnsafeParam("baz", "qux"), + )) + require.Equal(t, "Error: ✅\n", out.String()) + }) +} + +func ptr[T any](v T) *T { + return &v +} diff --git a/cobracli/go.mod b/cobracli/go.mod index 0ad307d02..94aea82c3 100644 --- a/cobracli/go.mod +++ b/cobracli/go.mod @@ -5,6 +5,7 @@ go 1.25.0 require ( github.com/nmiyake/pkg/errorstringer v1.0.0 github.com/palantir/pkg v1.1.0 + github.com/palantir/witchcraft-go-error v1.42.0 github.com/pkg/errors v0.8.1 github.com/spf13/cobra v1.10.1 github.com/stretchr/testify v1.11.1 @@ -13,6 +14,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/palantir/witchcraft-go-params v1.38.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.10 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/cobracli/go.sum b/cobracli/go.sum index 02a9d107e..1aae5e017 100644 --- a/cobracli/go.sum +++ b/cobracli/go.sum @@ -7,6 +7,10 @@ github.com/nmiyake/pkg/errorstringer v1.0.0 h1:i8VFMHpy2orAs8Lsoi7wOdQ6ipxOUe9GR github.com/nmiyake/pkg/errorstringer v1.0.0/go.mod h1:M7rsuKy+fiW7j812cNTScqf7kixe7k/ETY/+cbaqzRw= github.com/palantir/pkg v1.1.0 h1:0EhrSUP8oeeh3MUvk7V/UU7WmsN1UiJNTvNj0sN9Cpo= github.com/palantir/pkg v1.1.0/go.mod h1:KC9srP/9ssWRxBxFCIqhUGC4Jt7OJkWRz0Iqehup1/c= +github.com/palantir/witchcraft-go-error v1.42.0 h1:uQlWLAn6MSvj4JkOJeh9rAzPbkflOw1s7O1utVy6giw= +github.com/palantir/witchcraft-go-error v1.42.0/go.mod h1:VGIRpvW3a6tzWMyIM1vwPglDdz5I3Gy5NGxkSYlgSf0= +github.com/palantir/witchcraft-go-params v1.38.0 h1:PPyVF+GARkFjk6ID+8PuEQjkYtv+wuyG57uersVxvD4= +github.com/palantir/witchcraft-go-params v1.38.0/go.mod h1:gZinqdF9iMUIHgQMVGtVpQ9GIZuvG9s48oT/+nUm6vg= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/.bulldozer.yml b/cobracli/vendor/github.com/palantir/witchcraft-go-error/.bulldozer.yml new file mode 100644 index 000000000..b62e82baa --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/.bulldozer.yml @@ -0,0 +1,17 @@ +# Excavator auto-updates this file. Please contribute improvements to the central template. + +version: 1 +merge: + trigger: + labels: ["merge when ready"] + ignore: + labels: ["do not merge"] + method: squash + options: + squash: + body: pull_request_body + message_delimiter: ==COMMIT_MSG== + delete_after_merge: true +update: + trigger: + labels: ["update me"] diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/.changelog.yml b/cobracli/vendor/github.com/palantir/witchcraft-go-error/.changelog.yml new file mode 100644 index 000000000..a30143b32 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/.changelog.yml @@ -0,0 +1,3 @@ +# Excavator auto-updates this file. Please contribute improvements to the central template. + +# This file is intentionally empty. The file's existence enables changelog-app and is empty to use the default configuration. diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/.excavator.yml b/cobracli/vendor/github.com/palantir/witchcraft-go-error/.excavator.yml new file mode 100644 index 000000000..c1d8bf1af --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/.excavator.yml @@ -0,0 +1,11 @@ +# Excavator auto-updates this file. Please contribute improvements to the central template. + +auto-label: + names: + versions-props/upgrade-all: [ "merge when ready" ] + circleci/manage-circleci: [ "merge when ready" ] + tags: + donotmerge: [ "do not merge" ] + roomba: [ "merge when ready" ] + automerge: [ "merge when ready" ] + autorelease: [ "autorelease" ] diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/.gitignore b/cobracli/vendor/github.com/palantir/witchcraft-go-error/.gitignore new file mode 100644 index 000000000..e0bfc1cf1 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/.gitignore @@ -0,0 +1,5 @@ +*.iml +*.ipr +*.iws +.idea/ +/out/ diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/LICENSE b/cobracli/vendor/github.com/palantir/witchcraft-go-error/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/LICENSE @@ -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/cobracli/vendor/github.com/palantir/witchcraft-go-error/README.md b/cobracli/vendor/github.com/palantir/witchcraft-go-error/README.md new file mode 100644 index 000000000..9cfa83e57 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/README.md @@ -0,0 +1,20 @@ +

+Autorelease +

+ +witchcraft-go-error +=================== +[![](https://godoc.org/github.com/palantir/witchcraft-go-error?status.svg)](http://godoc.org/github.com/palantir/witchcraft-go-error) + +`witchcraft-error-go` defines the `werror` package, which provides an implementation of the `error` interface that +stores safe and unsafe parameters and has the ability to specify another error as a cause. + +Associating structured safe and unsafe parameters with an error allows other infrastructure such as logging to make +decisions about what parameters should or should not be extricated. + +TODO: +* Provide example usage and output in README + +License +------- +This project is made available under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0). diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/godelw b/cobracli/vendor/github.com/palantir/witchcraft-go-error/godelw new file mode 100644 index 000000000..b7d3b9ed2 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/godelw @@ -0,0 +1,262 @@ +#!/bin/bash + +set -euo pipefail + +# Version and checksums for godel. Values are populated by the godel "dist" task. +VERSION=2.137.0 +DARWIN_AMD64_CHECKSUM=36b638ba570aadd36e786673ca53a47d1d5c2c32cb69747fdccaeb17eb4cfaa6 +DARWIN_ARM64_CHECKSUM=f7b8b5f842b818b124b76416080847952a0e0a179ca743d618da49956e1a52da +LINUX_AMD64_CHECKSUM=837dec5b6222f2e12797819536c0333db40a573a452dd84c3af767be34bf2ebb +LINUX_ARM64_CHECKSUM=fab2aea38e211224c430132062cab4ac135c4e670126f41cdb2a548a173e6ec4 + +# Downloads file at URL to destination path using wget or curl. Prints an error and exits if wget or curl is not present. +function download { + local url=$1 + local dst=$2 + + # determine whether wget, curl or both are present + set +e + command -v wget >/dev/null 2>&1 + local wget_exists=$? + command -v curl >/dev/null 2>&1 + local curl_exists=$? + set -e + + # if one of wget or curl is not present, exit with error + if [ "$wget_exists" -ne 0 -a "$curl_exists" -ne 0 ]; then + echo "wget or curl must be present to download distribution. Install one of these programs and try again or install the distribution manually." + exit 1 + fi + + if [ "$wget_exists" -eq 0 ]; then + # attempt download using wget + echo "Downloading $url to $dst..." + local progress_opt="" + if wget --help | grep -q '\--show-progress'; then + progress_opt="-q --show-progress" + fi + set +e + wget -O "$dst" $progress_opt "$url" + rv=$? + set -e + if [ "$rv" -eq 0 ]; then + # success + return + fi + + echo "Download failed using command: wget -O $dst $progress_opt $url" + + # curl does not exist, so nothing more to try: exit + if [ "$curl_exists" -ne 0 ]; then + echo "Download failed using wget and curl was not found. Verify that the distribution URL is correct and try again or install the distribution manually." + exit 1 + fi + # curl exists, notify that download will be attempted using curl + echo "Attempting download using curl..." + fi + + # attempt download using curl + echo "Downloading $url to $dst..." + set +e + curl -f -L -o "$dst" "$url" + rv=$? + set -e + if [ "$rv" -ne 0 ]; then + echo "Download failed using command: curl -f -L -o $dst $url" + if [ "$wget_exists" -eq 0 ]; then + echo "Download failed using wget and curl. Verify that the distribution URL is correct and try again or install the distribution manually." + else + echo "Download failed using curl and wget was not found. Verify that the distribution URL is correct and try again or install the distribution manually." + fi + exit 1 + fi +} + +# verifies that the provided checksum matches the computed SHA-256 checksum of the specified file. If not, echoes an +# error and exits. +function verify_checksum { + local file=$1 + local expected_checksum=$2 + local computed_checksum=$(compute_sha256 $file) + if [ "$expected_checksum" != "$computed_checksum" ]; then + echo "SHA-256 checksum for $file did not match expected value." + echo "Expected: $expected_checksum" + echo "Actual: $computed_checksum" + exit 1 + fi +} + +# computes the SHA-256 hash of the provided file. Uses openssl, shasum or sha1sum program. +function compute_sha256 { + local file=$1 + if command -v openssl >/dev/null 2>&1; then + # print SHA-256 hash using openssl + openssl dgst -sha256 "$file" | sed -E 's/SHA(2-)?256\(.*\)= //' + elif command -v shasum >/dev/null 2>&1; then + # Darwin systems ship with "shasum" utility + shasum -a 256 "$file" | sed -E 's/[[:space:]]+.+//' + elif command -v sha256sum >/dev/null 2>&1; then + # Most Linux systems ship with sha256sum utility + sha256sum "$file" | sed -E 's/[[:space:]]+.+//' + else + echo "Could not find program to calculate SHA-256 checksum for file" + exit 1 + fi +} + +# Verifies that the tgz file at the provided path contains the paths/files that would be expected in a valid gödel +# distribution with the provided version. +function verify_dist_tgz_valid { + local tgz_path=$1 + local version=$2 + + local expected_paths=("godel-$version/" "godel-$version/bin/darwin-amd64/godel" "godel-$version/bin/darwin-arm64/godel" "godel-$version/bin/linux-amd64/godel" "godel-$version/bin/linux-arm64/godel" "godel-$version/wrapper/godelw" "godel-$version/wrapper/godel/config/") + local files=($(tar -tf "$tgz_path")) + + # this is a double-for loop, but fine since $expected_paths is small and bash doesn't have good primitives for set/map/list manipulation + for curr_line in "${files[@]}"; do + # if all expected paths have been found, terminate + if [[ ${#expected_paths[*]} == 0 ]]; then + break + fi + + # check for expected path and splice out if match is found + idx=0 + for curr_expected in "${expected_paths[@]}"; do + if [ "$curr_expected" = "$curr_line" ]; then + expected_paths=(${expected_paths[@]:0:idx} ${expected_paths[@]:$(($idx + 1))}) + break + fi + idx=$idx+1 + done + done + + # if any expected paths still remain, raise error and exit + if [[ ${#expected_paths[*]} > 0 ]]; then + echo "Required paths were not present in $tgz_path: ${expected_paths[@]}" + exit 1 + fi +} + +# Verifies that the gödel binary in the distribution reports the expected version when called with the "version" +# argument. Assumes that a valid gödel distribution directory for the given version exists in the provided directory. +function verify_godel_version { + local base_dir=$1 + local version=$2 + local os=$3 + local arch=$4 + + local expected_output="godel version $version" + local version_output=$($base_dir/godel-$version/bin/$os-$arch/godel version) + + if [ "$expected_output" != "$version_output" ]; then + echo "Version reported by godel executable did not match expected version: expected \"$expected_output\", was \"$version_output\"" + exit 1 + fi +} + +# directory of godelw script +SCRIPT_HOME=$(cd "$(dirname "$0")" && pwd) + +# use $GODEL_HOME or default value +GODEL_BASE_DIR=${GODEL_HOME:-$HOME/.godel} + +# determine OS +OS="" +EXPECTED_CHECKSUM="" +case "$(uname)-$(uname -m)" in + Darwin-x86_64) + OS=darwin + ARCH=amd64 + EXPECTED_CHECKSUM=$DARWIN_AMD64_CHECKSUM + ;; + Darwin-arm64) + OS=darwin + ARCH=arm64 + EXPECTED_CHECKSUM=$DARWIN_ARM64_CHECKSUM + ;; + Linux-x86_64) + OS=linux + ARCH=amd64 + EXPECTED_CHECKSUM=$LINUX_AMD64_CHECKSUM + ;; + Linux-aarch64) + OS=linux + ARCH=arm64 + EXPECTED_CHECKSUM=$LINUX_ARM64_CHECKSUM + ;; + *) + echo "Unsupported operating system-architecture: $(uname)-$(uname -m)" + exit 1 + ;; +esac + +# path to godel binary +CMD=$GODEL_BASE_DIR/dists/godel-$VERSION/bin/$OS-$ARCH/godel + +# godel binary is not present -- download distribution +if [ ! -f "$CMD" ]; then + # get download URL + PROPERTIES_FILE=$SCRIPT_HOME/godel/config/godel.properties + if [ ! -f "$PROPERTIES_FILE" ]; then + echo "Properties file must exist at $PROPERTIES_FILE" + exit 1 + fi + DOWNLOAD_URL=$(cat "$PROPERTIES_FILE" | sed -E -n "s/^distributionURL=//p") + if [ -z "$DOWNLOAD_URL" ]; then + echo "Value for property \"distributionURL\" was empty in $PROPERTIES_FILE" + exit 1 + fi + DOWNLOAD_CHECKSUM=$(cat "$PROPERTIES_FILE" | sed -E -n "s/^distributionSHA256=//p") + + # create downloads directory if it does not already exist + mkdir -p "$GODEL_BASE_DIR/downloads" + + # download tgz and verify its contents + # Download to unique location that includes PID ($$) and use trap ensure that temporary download file is cleaned up + # if script is terminated before the file is moved to its destination. + DOWNLOAD_DST=$GODEL_BASE_DIR/downloads/godel-$VERSION-$$.tgz + download "$DOWNLOAD_URL" "$DOWNLOAD_DST" + trap 'rm -rf "$DOWNLOAD_DST"' EXIT + if [ -n "$DOWNLOAD_CHECKSUM" ]; then + verify_checksum "$DOWNLOAD_DST" "$DOWNLOAD_CHECKSUM" + fi + verify_dist_tgz_valid "$DOWNLOAD_DST" "$VERSION" + + # create temporary directory for unarchiving, unarchive downloaded file and verify directory + TMP_DIST_DIR=$(mktemp -d "$GODEL_BASE_DIR/tmp_XXXXXX" 2>/dev/null || mktemp -d -t "$GODEL_BASE_DIR/tmp_XXXXXX") + trap 'rm -rf "$TMP_DIST_DIR"' EXIT + tar zxvf "$DOWNLOAD_DST" -C "$TMP_DIST_DIR" >/dev/null 2>&1 + verify_godel_version "$TMP_DIST_DIR" "$VERSION" "$OS" "$ARCH" + + # rename downloaded file to remove PID portion + mv "$DOWNLOAD_DST" "$GODEL_BASE_DIR/downloads/godel-$VERSION.tgz" + + # if destination directory for distribution already exists, remove it + if [ -d "$GODEL_BASE_DIR/dists/godel-$VERSION" ]; then + rm -rf "$GODEL_BASE_DIR/dists/godel-$VERSION" + fi + + # ensure that parent directory of destination exists + mkdir -p "$GODEL_BASE_DIR/dists" + + # move expanded distribution directory to destination location. The location of the unarchived directory is known to + # be in the same directory tree as the destination, so "mv" should always work. + mv "$TMP_DIST_DIR/godel-$VERSION" "$GODEL_BASE_DIR/dists/godel-$VERSION" + + # edge case cleanup: if the destination directory "$GODEL_BASE_DIR/dists/godel-$VERSION" was created prior to the + # "mv" operation above, then the move operation will move the source directory into the destination directory. In + # this case, remove the directory. It should always be safe to remove this directory because if the directory + # existed in the distribution and was non-empty, then the move operation would fail (because non-empty directories + # cannot be overwritten by mv). All distributions of a given version are also assumed to be identical. The only + # instance in which this would not work is if the distribution purposely contained an empty directory that matched + # the name "godel-$VERSION", and this is assumed to never be true. + if [ -d "$GODEL_BASE_DIR/dists/godel-$VERSION/godel-$VERSION" ]; then + rm -rf "$GODEL_BASE_DIR/dists/godel-$VERSION/godel-$VERSION" + fi +fi + +verify_checksum "$CMD" "$EXPECTED_CHECKSUM" + +# execute command +$CMD --wrapper "$SCRIPT_HOME/$(basename "$0")" "$@" diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/.gitignore b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/.gitignore @@ -0,0 +1,24 @@ +# 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/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/LICENSE b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/LICENSE new file mode 100644 index 000000000..835ba3e75 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/README.md b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/README.md new file mode 100644 index 000000000..273db3c98 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## Licence + +BSD-2-Clause diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/errors.go b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/errors.go new file mode 100644 index 000000000..b075a57b9 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/errors.go @@ -0,0 +1,269 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// and the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required the errors.WithStack and errors.WithMessage +// functions destructure errors.Wrap into its component operations of annotating +// an error with a stack trace and an a message, respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error which does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// causer interface is not exported by this package, but is considered a part +// of stable public API. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported +// +// %s print the error. If the error has a Cause it will be +// printed recursively +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface. +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// Where errors.StackTrace is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// stackTracer interface is not exported by this package, but is considered a part +// of stable public API. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + _, _ = io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + _, _ = io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + _, _ = io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is call, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + _, _ = io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + _, _ = io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/stack.go b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/stack.go new file mode 100644 index 000000000..0b73dcd06 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/internal/errors/stack.go @@ -0,0 +1,138 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s path of source file relative to the compile time GOPATH +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + _, _ = io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + _, _ = io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + _, _ = io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + _, _ = io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/params.go b/cobracli/vendor/github.com/palantir/witchcraft-go-error/params.go new file mode 100644 index 000000000..0fd9b283c --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/params.go @@ -0,0 +1,58 @@ +package werror + +import ( + wparams "github.com/palantir/witchcraft-go-params" +) + +type Param interface { + apply(*werror) +} + +type param func(*werror) + +func (p param) apply(e *werror) { + p(e) +} + +func SafeParam(key string, val interface{}) Param { + return SafeParams(map[string]interface{}{key: val}) +} + +func SafeParams(vals map[string]interface{}) Param { + return paramsHelper(vals, true) +} + +func UnsafeParam(key string, val interface{}) Param { + return UnsafeParams(map[string]interface{}{key: val}) +} + +func UnsafeParams(vals map[string]interface{}) Param { + return paramsHelper(vals, false) +} + +func paramsHelper(vals map[string]interface{}, safe bool) Param { + return param(func(z *werror) { + for k, v := range vals { + z.params[k] = paramValue{ + safe: safe, + value: v, + } + } + }) +} + +func SafeAndUnsafeParams(safe, unsafe map[string]interface{}) Param { + return param(func(z *werror) { + SafeParams(safe).apply(z) + UnsafeParams(unsafe).apply(z) + }) +} + +func Params(object wparams.ParamStorer) Param { + return param(func(z *werror) { + if object != nil { + SafeParams(object.SafeParams()).apply(z) + UnsafeParams(object.UnsafeParams()).apply(z) + } + }) +} diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/stacktrace.go b/cobracli/vendor/github.com/palantir/witchcraft-go-error/stacktrace.go new file mode 100644 index 000000000..cbb31796e --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/stacktrace.go @@ -0,0 +1,60 @@ +package werror + +import ( + "fmt" + "runtime" + + "github.com/palantir/witchcraft-go-error/internal/errors" +) + +var _ StackTrace = (*stack)(nil) + +// StackTrace provides formatting for an underlying stack trace. +type StackTrace interface { + fmt.Formatter +} + +// StackTracer provides the behavior necessary to retrieve a StackTrace formatter. +type StackTracer interface { + StackTrace() StackTrace +} + +// NewStackTrace creates a new StackTrace, constructed by collecting program counters from runtime callers. +func NewStackTrace() StackTrace { + return NewStackTraceWithSkip(1) +} + +// NewStackTraceWithSkip creates a new StackTrace that skips an additional `skip` stack frames. +func NewStackTraceWithSkip(skip int) StackTrace { + const depth = 32 + var pcs [depth]uintptr + // Changing this back to "3" by default. Most callers have only a single level of indirection. For newWerror + // specifically, which is always called indirectly, we now call this with skip of "1". + n := runtime.Callers(skip+3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(state fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case state.Flag('+'): + for _, pc := range *s { + f := errors.Frame(pc) + _, _ = fmt.Fprintf(state, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() errors.StackTrace { + f := make([]errors.Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = errors.Frame((*s)[i]) + } + return f +} diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/werror.go b/cobracli/vendor/github.com/palantir/witchcraft-go-error/werror.go new file mode 100644 index 000000000..05d6b0b9a --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/werror.go @@ -0,0 +1,368 @@ +// Package werror defines an error type that can store safe and unsafe parameters and can wrap other errors. +package werror + +import ( + "context" + "fmt" + + wparams "github.com/palantir/witchcraft-go-params" +) + +var _ Werror = (*werror)(nil) + +// Error is identical to calling ErrorWithContext with a context that does not have any wparams parameters. +// DEPRECATED: Please use ErrorWithContextParams instead to ensure that all the wparams parameters that are set on the +// context are included in the error. +func Error(msg string, params ...Param) error { + return newWerror(msg, nil, params...) +} + +// ErrorWithContextParams returns a new error with the provided message and parameters. The returned error also includes any +// wparams parameters that are stored in the context. +// +// The message should not contain any formatted parameters -- instead, use the SafeParam* or UnsafeParam* functions +// to create error parameters. +// +// Example: +// +// password, ok := config["password"] +// if !ok { +// return werror.ErrorWithContextParams(ctx, "configuration is missing password") +// } +func ErrorWithContextParams(ctx context.Context, msg string, params ...Param) error { + safe, unsafe := wparams.SafeAndUnsafeParamsFromContext(ctx) + fullParams := []Param{ + SafeParams(safe), + UnsafeParams(unsafe), + } + fullParams = append(fullParams, params...) + return newWerror(msg, nil, fullParams...) +} + +// Wrap is identical to calling WrapWithContextParams with a context that does not have any wparams parameters. +// DEPRECATED: Please use WrapWithContextParams instead to ensure that all the wparams parameters that are set on the +// context are included in the error. +func Wrap(err error, msg string, params ...Param) error { + if err == nil { + return nil + } + return newWerror(msg, err, params...) +} + +// WrapWithContextParams returns a new error with the provided message and stores the provided error as its cause. +// The returned error also includes any wparams parameters that are stored in the context. +// +// The message should not contain any formatted parameters -- instead use the SafeParam* or UnsafeParam* functions +// to create error parameters. +// +// Example: +// +// users, err := getUser(userID) +// if err != nil { +// return werror.WrapWithContextParams(ctx, err, "failed to get user", werror.SafeParam("userId", userID)) +// } +func WrapWithContextParams(ctx context.Context, err error, msg string, params ...Param) error { + if err == nil { + return nil + } + safe, unsafe := wparams.SafeAndUnsafeParamsFromContext(ctx) + fullParams := []Param{ + SafeParams(safe), + UnsafeParams(unsafe), + } + fullParams = append(fullParams, params...) + return newWerror(msg, err, fullParams...) +} + +// Convert err to werror error. +// +// If err is not a werror-based error, then a new werror error is created using the message from err. +// Otherwise, returns unchanged err. +// +// Example: +// +// file, err := os.Open("file.txt") +// if err != nil { +// return werror.Convert(err) +// } +func Convert(err error) error { + if err == nil { + return err + } + switch err.(type) { + case Werror: + return err + default: + return newWerror("", err) + } +} + +// RootCause returns the initial cause of an error. +// +// Traverses the cause hierarchy until it reaches an error which has no cause and returns that error. +func RootCause(err error) error { + for { + causer, ok := err.(Causer) + if !ok { + return err + } + cause := causer.Cause() + if cause == nil { + return err + } + err = cause + } +} + +// ParamsFromError returns all of the safe and unsafe parameters stored in the provided error. +// +// If the error implements the Causer interface, then the returned parameters will include all of the parameters stored +// in the causes as well. +// +// All of the keys and parameters of the map are flattened. +// +// Parameters are added from the outermost error to the innermost error. This means that, if multiple errors declare +// different values for the same keys, the values for the most specific (deepest) error will be the ones in the returned +// maps. +func ParamsFromError(err error) (safeParams map[string]interface{}, unsafeParams map[string]interface{}) { + safeParams = make(map[string]interface{}) + unsafeParams = make(map[string]interface{}) + if err != nil { + visitErrorParams(err, func(k string, v interface{}, safe bool) { + if safe { + safeParams[k] = v + } else { + unsafeParams[k] = v + } + }) + } + return safeParams, unsafeParams +} + +// ParamFromError returns the value of the parameter for the given key, or nil if no such key exists. Checks the +// parameters of the provided error and all of its causes. If the error and its causes contain multiple values for the +// same key, the most specific (deepest) value will be returned. +func ParamFromError(err error, key string) (value interface{}, safe bool) { + visitErrorParams(err, func(k string, v interface{}, s bool) { + if k == key { + value = v + safe = s + } + }) + return value, safe +} + +// visitErrorParams calls the provided visitor function on all of the parameters stored in the provided error and any of +// its causes. The function is invoked on all of the parameters stored in the provided error, then all of the parameters +// in the cause of the provided error, and so on. There are no guarantees made about the order in which the parameters +// will be called for a given error. +func visitErrorParams(err error, visitor func(k string, v interface{}, safe bool)) { + allErrs := []error{err} + for currErr := err; ; { + causer, ok := currErr.(Causer) + if !ok || causer.Cause() == nil { + // current error does not have a cause + break + } + allErrs = append(allErrs, causer.Cause()) + currErr = causer.Cause() + } + for _, currErr := range allErrs { + if ps, ok := currErr.(wparams.ParamStorer); ok { + for k, v := range ps.SafeParams() { + visitor(k, v, true) + } + for k, v := range ps.UnsafeParams() { + visitor(k, v, false) + } + } + } +} + +// Werror is an error type consisting of an underlying error, stacktrace, underlying causes, and safe and unsafe +// params associated with that error. +type Werror interface { + error + fmt.Formatter + Causer + StackTracer + wparams.ParamStorer + + Message() string +} + +// werror is an error type consisting of an underlying error and safe and unsafe params associated with that error. +type werror struct { + message string + cause error + stack StackTrace + params map[string]paramValue +} + +type paramValue struct { + safe bool + value interface{} +} + +// Causer interface is compatible with the interface used by pkg/errors. +type Causer interface { + Cause() error +} + +func newWerror(message string, cause error, params ...Param) error { + we := &werror{ + message: message, + cause: cause, + stack: NewStackTraceWithSkip(1), + params: make(map[string]paramValue), + } + for _, p := range params { + p.apply(we) + } + return we +} + +// Error returns the message for this error by delegating to the stored error. The error consists only of the message +// and does not include any other information such as safe/unsafe parameters or cause. +func (e *werror) Error() string { + if e.cause == nil { + return e.message + } + if e.message == "" { + return e.cause.Error() + } + return e.message + ": " + e.cause.Error() +} + +// Cause returns the underlying cause of this error or nil if there is none. +func (e *werror) Cause() error { + return e.cause +} + +// Unwrap returns the wrapped error. Exists to support Go error Is/As functions introduced in Go 1.13. +func (e *werror) Unwrap() error { + return e.cause +} + +// StackTrace returns the Stacktracer for this error or nil if there is none. +func (e *werror) StackTrace() StackTrace { + return e.stack +} + +// Message returns the message string for this error. +func (e *werror) Message() string { + return e.message +} + +// SafeParams returns params from this error and any underlying causes. If the error and its causes +// contain multiple values for the same key, the most specific (deepest) value will be returned. +func (e *werror) SafeParams() map[string]interface{} { + safe, _ := ParamsFromError(e.cause) + for k, v := range e.params { + if v.safe { + if _, exists := safe[k]; !exists { + safe[k] = v.value + } + } + } + return safe +} + +// UnsafeParams returns params from this error and any underlying causes. If the error and its causes +// contain multiple values for the same key, the most specific (deepest) value will be returned. +func (e *werror) UnsafeParams() map[string]interface{} { + _, unsafe := ParamsFromError(e.cause) + for k, v := range e.params { + if !v.safe { + if _, exists := unsafe[k]; !exists { + unsafe[k] = v.value + } + } + } + return unsafe +} + +// Format formats the error using the provided format state. Delegates to stored error. +func (e *werror) Format(state fmt.State, verb rune) { + safe := make(map[string]interface{}) + for k, v := range e.params { + if v.safe { + safe[k] = v.value + } + } + Format(e, safe, state, verb) +} + +// Format formats a Werror using the provided format state. This is a utility method that can +// be used by other implementations of Werror. The safeParams argument is expected to include +// safe params for this error only, not for any underlying causes. +func Format(err Werror, safeParams map[string]interface{}, state fmt.State, verb rune) { + if verb == 'v' && state.Flag('+') { + // Multi-line extra verbose format starts with cause first followed up by current error metadata. + formatCause(err, state, verb) + formatMessage(err, state, verb) + formatParameters(err, safeParams, state, verb) + formatStack(err, state, verb) + } else { + formatMessage(err, state, verb) + formatParameters(err, safeParams, state, verb) + formatStack(err, state, verb) + formatCause(err, state, verb) + } +} + +func formatMessage(err Werror, state fmt.State, verb rune) { + if err.Message() == "" { + return + } + switch verb { + case 's', 'q', 'v': + _, _ = fmt.Fprint(state, err.Message()) + } +} + +func formatParameters(err Werror, safeParams map[string]interface{}, state fmt.State, verb rune) { + if len(safeParams) == 0 { + return + } + if verb != 'v' { + return + } + if err.Message() != "" { + // Whitespace before the message. + _, _ = fmt.Fprint(state, " ") + } + _, _ = fmt.Fprintf(state, "%+v", safeParams) +} + +func formatStack(err Werror, state fmt.State, verb rune) { + if err.StackTrace() == nil { + return + } + if verb != 'v' || !state.Flag('+') { + return + } + err.StackTrace().Format(state, verb) +} + +func formatCause(err Werror, state fmt.State, verb rune) { + if err.Cause() == nil { + return + } + var prefix string + if err.Message() != "" || (verb == 'v' && len(err.SafeParams()) > 0) { + prefix = ": " + } + switch verb { + case 'v': + if state.Flag('+') { + _, _ = fmt.Fprintf(state, "%+v\n", err.Cause()) + } else { + _, _ = fmt.Fprintf(state, "%s%v", prefix, err.Cause()) + } + case 's': + _, _ = fmt.Fprintf(state, "%s%s", prefix, err.Cause()) + case 'q': + _, _ = fmt.Fprintf(state, "%s%q", prefix, err.Cause()) + } +} diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-error/werror_printer.go b/cobracli/vendor/github.com/palantir/witchcraft-go-error/werror_printer.go new file mode 100644 index 000000000..5687d5a26 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-error/werror_printer.go @@ -0,0 +1,109 @@ +package werror + +import ( + "bytes" + "fmt" + "reflect" + "sort" +) + +// GenerateErrorString will attempt to pretty print an error depending on its underlying type +// If it is a werror then: +// 1) Each message and params will be groups together on a separate line +// 2) Only the deepest werror stacktrace will be printed +// 3) GenerateErrorString will be called recursively to pretty print underlying errors as well +// If the error implements the fmt.Formatter interface, then it will be printed verbosely +// Otherwise, the error's underlying Error() function will be called and returned +func GenerateErrorString(err error, outputEveryCallingStack bool) string { + if werror, ok := err.(Werror); ok { + return generateWerrorString(werror, outputEveryCallingStack) + } + if fancy, ok := err.(fmt.Formatter); ok { + // This is a rich error type, like those produced by github.com/pkg/errors. + return fmt.Sprintf("%+v", fancy) + } + return err.Error() +} + +func generateWerrorString(err Werror, outputEveryCallingStack bool) string { + var buffer bytes.Buffer + writeMessage(err, &buffer) + writeParams(err, &buffer) + writeCause(err, &buffer, outputEveryCallingStack) + writeStack(err, &buffer, outputEveryCallingStack) + return buffer.String() +} + +func writeMessage(err Werror, buffer *bytes.Buffer) { + if err.Message() == "" { + return + } + buffer.WriteString(err.Message()) +} + +func writeParams(err Werror, buffer *bytes.Buffer) { + safeParams := getSafeParamsAtCurrentLevel(err) + var safeKeys []string + for k := range safeParams { + safeKeys = append(safeKeys, k) + } + sort.Strings(safeKeys) + messageAndParams := err.Message() != "" && len(safeParams) != 0 + messageOrParams := err.Message() != "" || len(safeParams) != 0 + if messageAndParams { + buffer.WriteString(" ") + } + for _, safeKey := range safeKeys { + safeValue := safeParams[safeKey] + if v := reflect.ValueOf(safeValue); v.Kind() == reflect.Ptr && !v.IsNil() { + safeValue = v.Elem().Interface() + } + buffer.WriteString(fmt.Sprintf("%+v:%+v", safeKey, safeValue)) + // If it is not the last param, add a separator + if !(safeKeys[len(safeKeys)-1] == safeKey) { + buffer.WriteString(", ") + } + } + if messageOrParams { + buffer.WriteString("\n") + } +} + +func getSafeParamsAtCurrentLevel(err Werror) map[string]interface{} { + safeParamsAtThisLevel := make(map[string]interface{}, 0) + childSafeParams := getChildSafeParams(err) + for k, v := range err.SafeParams() { + _, ok := childSafeParams[k] + if ok { + continue + } + safeParamsAtThisLevel[k] = v + } + return safeParamsAtThisLevel +} + +func getChildSafeParams(err Werror) map[string]interface{} { + if err.Cause() == nil { + return make(map[string]interface{}, 0) + } + causeAsWerror, ok := err.Cause().(Werror) + if !ok { + return make(map[string]interface{}, 0) + } + return causeAsWerror.SafeParams() +} + +func writeCause(err Werror, buffer *bytes.Buffer, outputEveryCallingStack bool) { + if err.Cause() != nil { + buffer.WriteString(GenerateErrorString(err.Cause(), outputEveryCallingStack)) + } +} + +func writeStack(err Werror, buffer *bytes.Buffer, outputEveryCallingStack bool) { + if _, ok := err.Cause().(Werror); ok { + if !outputEveryCallingStack { + return + } + } + buffer.WriteString(fmt.Sprintf("%+v", err.StackTrace())) +} diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-params/.bulldozer.yml b/cobracli/vendor/github.com/palantir/witchcraft-go-params/.bulldozer.yml new file mode 100644 index 000000000..b62e82baa --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-params/.bulldozer.yml @@ -0,0 +1,17 @@ +# Excavator auto-updates this file. Please contribute improvements to the central template. + +version: 1 +merge: + trigger: + labels: ["merge when ready"] + ignore: + labels: ["do not merge"] + method: squash + options: + squash: + body: pull_request_body + message_delimiter: ==COMMIT_MSG== + delete_after_merge: true +update: + trigger: + labels: ["update me"] diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-params/.changelog.yml b/cobracli/vendor/github.com/palantir/witchcraft-go-params/.changelog.yml new file mode 100644 index 000000000..a30143b32 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-params/.changelog.yml @@ -0,0 +1,3 @@ +# Excavator auto-updates this file. Please contribute improvements to the central template. + +# This file is intentionally empty. The file's existence enables changelog-app and is empty to use the default configuration. diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-params/.excavator.yml b/cobracli/vendor/github.com/palantir/witchcraft-go-params/.excavator.yml new file mode 100644 index 000000000..c1d8bf1af --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-params/.excavator.yml @@ -0,0 +1,11 @@ +# Excavator auto-updates this file. Please contribute improvements to the central template. + +auto-label: + names: + versions-props/upgrade-all: [ "merge when ready" ] + circleci/manage-circleci: [ "merge when ready" ] + tags: + donotmerge: [ "do not merge" ] + roomba: [ "merge when ready" ] + automerge: [ "merge when ready" ] + autorelease: [ "autorelease" ] diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-params/.gitignore b/cobracli/vendor/github.com/palantir/witchcraft-go-params/.gitignore new file mode 100644 index 000000000..e0bfc1cf1 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-params/.gitignore @@ -0,0 +1,5 @@ +*.iml +*.ipr +*.iws +.idea/ +/out/ diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-params/LICENSE b/cobracli/vendor/github.com/palantir/witchcraft-go-params/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-params/LICENSE @@ -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/cobracli/vendor/github.com/palantir/witchcraft-go-params/README.md b/cobracli/vendor/github.com/palantir/witchcraft-go-params/README.md new file mode 100644 index 000000000..78b38cc26 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-params/README.md @@ -0,0 +1,77 @@ +

+Autorelease +

+ +witchcraft-go-params +==================== +[![](https://godoc.org/github.com/palantir/witchcraft-go-params?status.svg)](http://godoc.org/github.com/palantir/witchcraft-go-params) + +`witchcraft-go-params` defines the `wparams` package, which provides the `ParamStorer` interface and functions for +storing and retrieving `ParamStorer` implementations in from a context. + +Conceptually, "params" are values that are associated with a specific key that provide context about an operation that +is being performed. Params are categorized as "safe" or "unsafe" -- "safe" params are parameters which are considered +safe to ship/export/expose off-premises, while "unsafe" parameters are parameters that should not leave the premises. +Param values are typically used by things such as loggers and errors to provide further context for an operation. Keys +are case-sensitive and must be unique across both safe and unsafe parameters. + +The following is a short example of a canonical use case: + +```go +type UserID int64 + +func UpdateUserInfo(ctx context.Context, userID UserID, info UserInfo) error { + ctx = wparams.ContextWithSafeParam(ctx, "userId", userID) + + svc1log.FromContext(ctx).Info("Updating user information", svc1log.Params(wparams.ParamsFromContext(ctx))) + if err := validateInput(ctx, info); err != nil { + return err + } + // ... + return nil +} + +func validateInput(ctx context.Context, info UserInfo) error { + if info.Name == "" { + return werror.Error("invalid user info", werror.Params(wparams.ParamsFromContext(ctx))) + } + // ... + return nil +} +``` + +In this example, "userId" and its value are registered as a safe parameter on the context at the beginning of the +`UpdateUserInfo` function. This function (and the other functions that it calls) extract the safe and unsafe parameters +from the context when performing operations such as logging or creating errors. This makes it such that the logger +messages and errors are provided with all of the relevant parameters for their operation, along with the knowledge of +whether the parameters are safe and unsafe. + +If a specific type will be recorded as a parameter often and there is a sensible default value for its name and +safe/unsafe status, it may make sense to have the type implement the `ParamStorer` interface directly. For example, if +`UserID` is known to be safe and should always be recorded as "userId", the example above could be updated as follows: + +```go +type UserID int64 + +func (id UserID) SafeParams() map[string]interface{} { + return map[string]interface{}{ + "userId": id, + } +} + +func (id UserID) UnsafeParams() map[string]interface{} { + return nil +} + +func UpdateUserInfo(ctx context.Context, userID UserID, info UserInfo) error { + ctx = wparams.ContextWithParams(ctx, userID) + // the rest is the same as the first example +} +``` + +This pattern allows a type to dictate its default behavior for its name and whether it is safe/unsafe, which makes it +easier to ensure that the parameter is consistent across various usages. + +License +------- +This project is made available under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0). diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-params/godelw b/cobracli/vendor/github.com/palantir/witchcraft-go-params/godelw new file mode 100644 index 000000000..576dd2d55 --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-params/godelw @@ -0,0 +1,262 @@ +#!/bin/bash + +set -euo pipefail + +# Version and checksums for godel. Values are populated by the godel "dist" task. +VERSION=2.127.0 +DARWIN_AMD64_CHECKSUM=3e82d02d9e415c76992a2d9793e765172c180dc3570bd3a3becef903d37d88df +DARWIN_ARM64_CHECKSUM=bc715f6ea47ce0815e78890a1b85971ede98cd654533ff1e90e458fe09eedaa0 +LINUX_AMD64_CHECKSUM=1b7e8561d1f3cd98bbea098a06be096aed886a832a525cd993faade33411e736 +LINUX_ARM64_CHECKSUM=83d2bfab3f39d6795bfa9ed0d336552ec3f0ae321acdc35e3f9dd72a96a8c919 + +# Downloads file at URL to destination path using wget or curl. Prints an error and exits if wget or curl is not present. +function download { + local url=$1 + local dst=$2 + + # determine whether wget, curl or both are present + set +e + command -v wget >/dev/null 2>&1 + local wget_exists=$? + command -v curl >/dev/null 2>&1 + local curl_exists=$? + set -e + + # if one of wget or curl is not present, exit with error + if [ "$wget_exists" -ne 0 -a "$curl_exists" -ne 0 ]; then + echo "wget or curl must be present to download distribution. Install one of these programs and try again or install the distribution manually." + exit 1 + fi + + if [ "$wget_exists" -eq 0 ]; then + # attempt download using wget + echo "Downloading $url to $dst..." + local progress_opt="" + if wget --help | grep -q '\--show-progress'; then + progress_opt="-q --show-progress" + fi + set +e + wget -O "$dst" $progress_opt "$url" + rv=$? + set -e + if [ "$rv" -eq 0 ]; then + # success + return + fi + + echo "Download failed using command: wget -O $dst $progress_opt $url" + + # curl does not exist, so nothing more to try: exit + if [ "$curl_exists" -ne 0 ]; then + echo "Download failed using wget and curl was not found. Verify that the distribution URL is correct and try again or install the distribution manually." + exit 1 + fi + # curl exists, notify that download will be attempted using curl + echo "Attempting download using curl..." + fi + + # attempt download using curl + echo "Downloading $url to $dst..." + set +e + curl -f -L -o "$dst" "$url" + rv=$? + set -e + if [ "$rv" -ne 0 ]; then + echo "Download failed using command: curl -f -L -o $dst $url" + if [ "$wget_exists" -eq 0 ]; then + echo "Download failed using wget and curl. Verify that the distribution URL is correct and try again or install the distribution manually." + else + echo "Download failed using curl and wget was not found. Verify that the distribution URL is correct and try again or install the distribution manually." + fi + exit 1 + fi +} + +# verifies that the provided checksum matches the computed SHA-256 checksum of the specified file. If not, echoes an +# error and exits. +function verify_checksum { + local file=$1 + local expected_checksum=$2 + local computed_checksum=$(compute_sha256 $file) + if [ "$expected_checksum" != "$computed_checksum" ]; then + echo "SHA-256 checksum for $file did not match expected value." + echo "Expected: $expected_checksum" + echo "Actual: $computed_checksum" + exit 1 + fi +} + +# computes the SHA-256 hash of the provided file. Uses openssl, shasum or sha1sum program. +function compute_sha256 { + local file=$1 + if command -v openssl >/dev/null 2>&1; then + # print SHA-256 hash using openssl + openssl dgst -sha256 "$file" | sed -E 's/SHA(2-)?256\(.*\)= //' + elif command -v shasum >/dev/null 2>&1; then + # Darwin systems ship with "shasum" utility + shasum -a 256 "$file" | sed -E 's/[[:space:]]+.+//' + elif command -v sha256sum >/dev/null 2>&1; then + # Most Linux systems ship with sha256sum utility + sha256sum "$file" | sed -E 's/[[:space:]]+.+//' + else + echo "Could not find program to calculate SHA-256 checksum for file" + exit 1 + fi +} + +# Verifies that the tgz file at the provided path contains the paths/files that would be expected in a valid gödel +# distribution with the provided version. +function verify_dist_tgz_valid { + local tgz_path=$1 + local version=$2 + + local expected_paths=("godel-$version/" "godel-$version/bin/darwin-amd64/godel" "godel-$version/bin/darwin-arm64/godel" "godel-$version/bin/linux-amd64/godel" "godel-$version/bin/linux-arm64/godel" "godel-$version/wrapper/godelw" "godel-$version/wrapper/godel/config/") + local files=($(tar -tf "$tgz_path")) + + # this is a double-for loop, but fine since $expected_paths is small and bash doesn't have good primitives for set/map/list manipulation + for curr_line in "${files[@]}"; do + # if all expected paths have been found, terminate + if [[ ${#expected_paths[*]} == 0 ]]; then + break + fi + + # check for expected path and splice out if match is found + idx=0 + for curr_expected in "${expected_paths[@]}"; do + if [ "$curr_expected" = "$curr_line" ]; then + expected_paths=(${expected_paths[@]:0:idx} ${expected_paths[@]:$(($idx + 1))}) + break + fi + idx=$idx+1 + done + done + + # if any expected paths still remain, raise error and exit + if [[ ${#expected_paths[*]} > 0 ]]; then + echo "Required paths were not present in $tgz_path: ${expected_paths[@]}" + exit 1 + fi +} + +# Verifies that the gödel binary in the distribution reports the expected version when called with the "version" +# argument. Assumes that a valid gödel distribution directory for the given version exists in the provided directory. +function verify_godel_version { + local base_dir=$1 + local version=$2 + local os=$3 + local arch=$4 + + local expected_output="godel version $version" + local version_output=$($base_dir/godel-$version/bin/$os-$arch/godel version) + + if [ "$expected_output" != "$version_output" ]; then + echo "Version reported by godel executable did not match expected version: expected \"$expected_output\", was \"$version_output\"" + exit 1 + fi +} + +# directory of godelw script +SCRIPT_HOME=$(cd "$(dirname "$0")" && pwd) + +# use $GODEL_HOME or default value +GODEL_BASE_DIR=${GODEL_HOME:-$HOME/.godel} + +# determine OS +OS="" +EXPECTED_CHECKSUM="" +case "$(uname)-$(uname -m)" in + Darwin-x86_64) + OS=darwin + ARCH=amd64 + EXPECTED_CHECKSUM=$DARWIN_AMD64_CHECKSUM + ;; + Darwin-arm64) + OS=darwin + ARCH=arm64 + EXPECTED_CHECKSUM=$DARWIN_ARM64_CHECKSUM + ;; + Linux-x86_64) + OS=linux + ARCH=amd64 + EXPECTED_CHECKSUM=$LINUX_AMD64_CHECKSUM + ;; + Linux-aarch64) + OS=linux + ARCH=arm64 + EXPECTED_CHECKSUM=$LINUX_ARM64_CHECKSUM + ;; + *) + echo "Unsupported operating system-architecture: $(uname)-$(uname -m)" + exit 1 + ;; +esac + +# path to godel binary +CMD=$GODEL_BASE_DIR/dists/godel-$VERSION/bin/$OS-$ARCH/godel + +# godel binary is not present -- download distribution +if [ ! -f "$CMD" ]; then + # get download URL + PROPERTIES_FILE=$SCRIPT_HOME/godel/config/godel.properties + if [ ! -f "$PROPERTIES_FILE" ]; then + echo "Properties file must exist at $PROPERTIES_FILE" + exit 1 + fi + DOWNLOAD_URL=$(cat "$PROPERTIES_FILE" | sed -E -n "s/^distributionURL=//p") + if [ -z "$DOWNLOAD_URL" ]; then + echo "Value for property \"distributionURL\" was empty in $PROPERTIES_FILE" + exit 1 + fi + DOWNLOAD_CHECKSUM=$(cat "$PROPERTIES_FILE" | sed -E -n "s/^distributionSHA256=//p") + + # create downloads directory if it does not already exist + mkdir -p "$GODEL_BASE_DIR/downloads" + + # download tgz and verify its contents + # Download to unique location that includes PID ($$) and use trap ensure that temporary download file is cleaned up + # if script is terminated before the file is moved to its destination. + DOWNLOAD_DST=$GODEL_BASE_DIR/downloads/godel-$VERSION-$$.tgz + download "$DOWNLOAD_URL" "$DOWNLOAD_DST" + trap 'rm -rf "$DOWNLOAD_DST"' EXIT + if [ -n "$DOWNLOAD_CHECKSUM" ]; then + verify_checksum "$DOWNLOAD_DST" "$DOWNLOAD_CHECKSUM" + fi + verify_dist_tgz_valid "$DOWNLOAD_DST" "$VERSION" + + # create temporary directory for unarchiving, unarchive downloaded file and verify directory + TMP_DIST_DIR=$(mktemp -d "$GODEL_BASE_DIR/tmp_XXXXXX" 2>/dev/null || mktemp -d -t "$GODEL_BASE_DIR/tmp_XXXXXX") + trap 'rm -rf "$TMP_DIST_DIR"' EXIT + tar zxvf "$DOWNLOAD_DST" -C "$TMP_DIST_DIR" >/dev/null 2>&1 + verify_godel_version "$TMP_DIST_DIR" "$VERSION" "$OS" "$ARCH" + + # rename downloaded file to remove PID portion + mv "$DOWNLOAD_DST" "$GODEL_BASE_DIR/downloads/godel-$VERSION.tgz" + + # if destination directory for distribution already exists, remove it + if [ -d "$GODEL_BASE_DIR/dists/godel-$VERSION" ]; then + rm -rf "$GODEL_BASE_DIR/dists/godel-$VERSION" + fi + + # ensure that parent directory of destination exists + mkdir -p "$GODEL_BASE_DIR/dists" + + # move expanded distribution directory to destination location. The location of the unarchived directory is known to + # be in the same directory tree as the destination, so "mv" should always work. + mv "$TMP_DIST_DIR/godel-$VERSION" "$GODEL_BASE_DIR/dists/godel-$VERSION" + + # edge case cleanup: if the destination directory "$GODEL_BASE_DIR/dists/godel-$VERSION" was created prior to the + # "mv" operation above, then the move operation will move the source directory into the destination directory. In + # this case, remove the directory. It should always be safe to remove this directory because if the directory + # existed in the distribution and was non-empty, then the move operation would fail (because non-empty directories + # cannot be overwritten by mv). All distributions of a given version are also assumed to be identical. The only + # instance in which this would not work is if the distribution purposely contained an empty directory that matched + # the name "godel-$VERSION", and this is assumed to never be true. + if [ -d "$GODEL_BASE_DIR/dists/godel-$VERSION/godel-$VERSION" ]; then + rm -rf "$GODEL_BASE_DIR/dists/godel-$VERSION/godel-$VERSION" + fi +fi + +verify_checksum "$CMD" "$EXPECTED_CHECKSUM" + +# execute command +$CMD --wrapper "$SCRIPT_HOME/$(basename "$0")" "$@" diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-params/paramstorer.go b/cobracli/vendor/github.com/palantir/witchcraft-go-params/paramstorer.go new file mode 100644 index 000000000..77598afdd --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-params/paramstorer.go @@ -0,0 +1,145 @@ +package wparams + +// ParamStorer is a type that stores safe and unsafe parameters. Keys should be unique across both SafeParams and +// UnsafeParams (that is, if a key occurs in one map, it should not occur in the other). For performance reasons, +// the maps returned by SafeParams and UnsafeParams are references to the underlying storage and should not be modified +// by the caller. +type ParamStorer interface { + SafeParams() map[string]interface{} + UnsafeParams() map[string]interface{} +} + +// NewParamStorer returns a new ParamStorer that stores all of the params in the provided ParamStorer inputs. The params +// are added from the param storers in the order in which they are provided, and for each individual param storer all of +// the safe params are added before the unsafe params while maintaining key uniqueness across both safe and unsafe +// parameters. This means that, if the same parameter is provided by multiple ParamStorer inputs, the returned +// ParamStorer will have the key (including safe/unsafe type) and value as provided by the last ParamStorer (for +// example, if an unsafe key/value pair is provided by one ParamStorer and a later ParamStorer specifies a safe +// key/value pair with the same key, the returned ParamStorer will store the last safe key/value pair). +func NewParamStorer(paramStorers ...ParamStorer) ParamStorer { + collector := &mapParamStorer{} + for _, storer := range paramStorers { + collector.copyFrom(storer) + } + return collector +} + +// NewSafeParamStorer returns a new ParamStorer that stores the provided parameters as SafeParams. +func NewSafeParamStorer(safeParams map[string]interface{}) ParamStorer { + return NewSafeAndUnsafeParamStorer(safeParams, nil) +} + +// NewSafeParam returns a new ParamStorer that stores a single safe parameter. +func NewSafeParam(key string, value interface{}) ParamStorer { + return singleParamStorer{key: key, value: value, safe: true} +} + +// NewUnsafeParamStorer returns a new ParamStorer that stores the provided parameters as UnsafeParams. +func NewUnsafeParamStorer(unsafeParams map[string]interface{}) ParamStorer { + return NewSafeAndUnsafeParamStorer(nil, unsafeParams) +} + +// NewUnsafeParam returns a new ParamStorer that stores a single unsafe parameter. +func NewUnsafeParam(key string, value interface{}) ParamStorer { + return singleParamStorer{key: key, value: value, safe: false} +} + +// NewSafeAndUnsafeParamStorer returns a new ParamStorer that stores the provided safe parameters as SafeParams and the +// unsafe parameters as UnsafeParams. If the safeParams and unsafeParams have any keys in common, the key/value pairs in +// the unsafeParams will be used (the conflicting key/value pairs provided by safeParams will be ignored). +func NewSafeAndUnsafeParamStorer(safeParams, unsafeParams map[string]interface{}) ParamStorer { + storer := &mapParamStorer{} + for k, v := range safeParams { + storer.putSafeParam(k, v) + } + for k, v := range unsafeParams { + storer.putUnsafeParam(k, v) + } + return storer +} + +type mapParamStorer struct { + safeParams map[string]interface{} + unsafeParams map[string]interface{} +} + +func (m *mapParamStorer) SafeParams() map[string]interface{} { + if m.safeParams == nil { + return map[string]interface{}{} + } + return m.safeParams +} + +func (m *mapParamStorer) UnsafeParams() map[string]interface{} { + if m.unsafeParams == nil { + return map[string]interface{}{} + } + return m.unsafeParams +} + +func (m *mapParamStorer) putSafeParam(k string, v interface{}) { + if m.safeParams == nil { + m.safeParams = map[string]interface{}{k: v} + } else { + m.safeParams[k] = v + } + delete(m.unsafeParams, k) +} + +func (m *mapParamStorer) putUnsafeParam(k string, v interface{}) { + if m.unsafeParams == nil { + m.unsafeParams = map[string]interface{}{k: v} + } else { + m.unsafeParams[k] = v + } + delete(m.safeParams, k) +} + +func (m *mapParamStorer) copyFrom(storer ParamStorer) { + if storer == nil { + return + } + // If this is one of our types, we can access the values directly and avoid intermediate map allocations. + switch st := storer.(type) { + case singleParamStorer: + if st.safe { + m.putSafeParam(st.key, st.value) + } else { + m.putUnsafeParam(st.key, st.value) + } + case *mapParamStorer: + for k, v := range st.safeParams { + m.putSafeParam(k, v) + } + for k, v := range st.unsafeParams { + m.putUnsafeParam(k, v) + } + default: + for k, v := range st.SafeParams() { + m.putSafeParam(k, v) + } + for k, v := range st.UnsafeParams() { + m.putUnsafeParam(k, v) + } + } +} + +type singleParamStorer struct { + key string + value interface{} + safe bool +} + +func (s singleParamStorer) SafeParams() map[string]interface{} { + if !s.safe { + return map[string]interface{}{} + } + return map[string]interface{}{s.key: s.value} +} + +func (s singleParamStorer) UnsafeParams() map[string]interface{} { + if s.safe { + return map[string]interface{}{} + } + return map[string]interface{}{s.key: s.value} +} diff --git a/cobracli/vendor/github.com/palantir/witchcraft-go-params/paramstorer_context.go b/cobracli/vendor/github.com/palantir/witchcraft-go-params/paramstorer_context.go new file mode 100644 index 000000000..e376bf4aa --- /dev/null +++ b/cobracli/vendor/github.com/palantir/witchcraft-go-params/paramstorer_context.go @@ -0,0 +1,72 @@ +package wparams + +import ( + "context" +) + +type witchcraftParamsContextKeyType string + +const wParamsContextKey = witchcraftParamsContextKeyType("witchcraftParams") + +// ContextWithParamStorers returns a copy of the provided context that contains all of the safe and unsafe parameters +// provided by the provided ParamStorers. If the provided context already has safe/unsafe params, the newly returned +// context will contain the result of merging the previous parameters with the provided parameters. +func ContextWithParamStorers(ctx context.Context, params ...ParamStorer) context.Context { + return context.WithValue(ctx, wParamsContextKey, NewParamStorer(append([]ParamStorer{ParamStorerFromContext(ctx)}, params...)...)) +} + +// ContextWithSafeParam returns a copy of the provided context that contains the provided safe parameter. If the +// provided context already has safe/unsafe params, the newly returned context will contain the result of merging the +// previous parameters with the provided parameter. +func ContextWithSafeParam(ctx context.Context, key string, value interface{}) context.Context { + return ContextWithParamStorers(ctx, NewSafeParam(key, value)) +} + +// ContextWithSafeParams returns a copy of the provided context that contains the provided safe parameters. If the +// provided context already has safe/unsafe params, the newly returned context will contain the result of merging the +// previous parameters with the provided parameters. +func ContextWithSafeParams(ctx context.Context, safeParams map[string]interface{}) context.Context { + return ContextWithParamStorers(ctx, NewSafeParamStorer(safeParams)) +} + +// ContextWithUnsafeParam returns a copy of the provided context that contains the provided unsafe parameter. If the +// provided context already has safe/unsafe params, the newly returned context will contain the result of merging the +// previous parameters with the provided parameter. +func ContextWithUnsafeParam(ctx context.Context, key string, value interface{}) context.Context { + return ContextWithParamStorers(ctx, NewUnsafeParam(key, value)) +} + +// ContextWithUnsafeParams returns a copy of the provided context that contains the provided unsafe parameters. If the +// provided context already has safe/unsafe params, the newly returned context will contain the result of merging the +// previous parameters with the provided parameters. +func ContextWithUnsafeParams(ctx context.Context, unsafeParams map[string]interface{}) context.Context { + return ContextWithParamStorers(ctx, NewUnsafeParamStorer(unsafeParams)) +} + +// ContextWithSafeAndUnsafeParams returns a copy of the provided context that contains the provided safe and unsafe +// parameters. If the provided context already has safe/unsafe params, the newly returned context will contain the +// result of merging the previous parameters with the provided parameters. +func ContextWithSafeAndUnsafeParams(ctx context.Context, safeParams, unsafeParams map[string]interface{}) context.Context { + return ContextWithParamStorers(ctx, NewSafeAndUnsafeParamStorer(safeParams, unsafeParams)) +} + +// ParamStorerFromContext returns the ParamStorer stored in the provided context. Returns nil if the provided context +// does not contain a ParamStorer. +func ParamStorerFromContext(ctx context.Context) ParamStorer { + val := ctx.Value(wParamsContextKey) + if paramStorer, ok := val.(ParamStorer); ok { + return paramStorer + } + return nil +} + +// SafeAndUnsafeParamsFromContext returns the safe and unsafe parameters stored in the ParamStorer returned by +// ParamStorerFromContext for the provided context. Returns nil maps if the provided context does not have a +// ParamStorer. +func SafeAndUnsafeParamsFromContext(ctx context.Context) (safeParams map[string]interface{}, unsafeParams map[string]interface{}) { + storer := ParamStorerFromContext(ctx) + if storer == nil { + return nil, nil + } + return storer.SafeParams(), storer.UnsafeParams() +} diff --git a/cobracli/vendor/modules.txt b/cobracli/vendor/modules.txt index 24ffa5566..01312bbc7 100644 --- a/cobracli/vendor/modules.txt +++ b/cobracli/vendor/modules.txt @@ -10,6 +10,13 @@ github.com/nmiyake/pkg/errorstringer # github.com/palantir/pkg v1.1.0 ## explicit; go 1.19 github.com/palantir/pkg +# github.com/palantir/witchcraft-go-error v1.42.0 +## explicit; go 1.25.0 +github.com/palantir/witchcraft-go-error +github.com/palantir/witchcraft-go-error/internal/errors +# github.com/palantir/witchcraft-go-params v1.38.0 +## explicit; go 1.24.0 +github.com/palantir/witchcraft-go-params # github.com/pkg/errors v0.8.1 ## explicit github.com/pkg/errors